home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume23 / trn / part06 < prev    next >
Encoding:
Text File  |  1991-08-22  |  64.4 KB  |  2,400 lines

  1. This is a new archive version of TRN at patchlevel 3.
  2. The original posting took up Volume23, issues 60 to 73, with
  3. various problems.  These files replace those issues.
  4.  
  5. #! /bin/sh
  6. # This is a shell archive.  Remove anything before this line, then feed it
  7. # into a shell via "sh file" or similar.  To overwrite existing files,
  8. # type "sh file -c".
  9. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  10. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  11. # Contents:  INIT cheat.c common.h mthreads.c
  12. # Wrapped by rsalz@litchi.bbn.com on Fri Aug 23 16:38:55 1991
  13. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  14. echo If this archive is complete, you will see the following message:
  15. echo '          "shar: End of archive 6 (of 14)."'
  16. if test -f 'INIT' -a "${1}" != "-c" ; then 
  17.   echo shar: Will not clobber existing file \"'INIT'\"
  18. else
  19.   echo shar: Extracting \"'INIT'\" \(18 characters\)
  20.   sed "s/^X//" >'INIT' <<'END_OF_FILE'
  21. X-ESAMPLE="sample"
  22. END_OF_FILE
  23.   if test 18 -ne `wc -c <'INIT'`; then
  24.     echo shar: \"'INIT'\" unpacked with wrong size!
  25.   fi
  26.   # end of 'INIT'
  27. fi
  28. if test -f 'cheat.c' -a "${1}" != "-c" ; then 
  29.   echo shar: Will not clobber existing file \"'cheat.c'\"
  30. else
  31.   echo shar: Extracting \"'cheat.c'\" \(3117 characters\)
  32.   sed "s/^X//" >'cheat.c' <<'END_OF_FILE'
  33. X/* $Header: cheat.c,v 4.3.3.1 90/07/21 20:14:01 davison Trn $
  34. X *
  35. X * $Log:    cheat.c,v $
  36. X * Revision 4.3.3.1  90/07/21  20:14:01  davison
  37. X * Initial Trn Release
  38. X * 
  39. X * Revision 4.3.2.2  89/11/27  01:30:18  sob
  40. X * Altered NNTP code per ideas suggested by Bela Lubkin
  41. X * <filbo@gorn.santa-cruz.ca.us>
  42. X * 
  43. X * Revision 4.3.2.1  89/11/26  22:54:21  sob
  44. X * Added RRN support
  45. X * 
  46. X * Revision 4.3  85/05/01  11:36:46  lwall
  47. X * Baseline for release with 4.3bsd.
  48. X * 
  49. X */
  50. X
  51. X#include "EXTERN.h"
  52. X#include "common.h"
  53. X#include "intrp.h"
  54. X#include "search.h"
  55. X#include "ng.h"
  56. X#include "bits.h"
  57. X#include "artio.h"
  58. X#include "term.h"
  59. X#include "artsrch.h"
  60. X#include "head.h"
  61. X#include "INTERN.h"
  62. X#include "cheat.h"
  63. X
  64. X/* see what we can do while they are reading */
  65. X
  66. X#ifdef PENDING
  67. X#   ifdef ARTSEARCH
  68. X    COMPEX srchcompex;        /* compiled regex for searchahead */
  69. X#   endif
  70. X#endif
  71. X
  72. Xvoid
  73. Xcheat_init()
  74. X{
  75. X    ;
  76. X}
  77. X
  78. X#ifdef PENDING
  79. Xvoid
  80. Xlook_ahead()
  81. X{
  82. X#ifdef ARTSEARCH
  83. X    register char *h, *s;
  84. X
  85. X#ifdef DEBUGGING
  86. X    if (debug && srchahead) {
  87. X    printf("(%ld)",(long)srchahead);
  88. X    fflush(stdout);
  89. X    }
  90. X#endif
  91. X    if (srchahead && srchahead < art) {    /* in ^N mode? */
  92. X    char *pattern;
  93. X
  94. X    pattern = buf+1;
  95. X    strcpy(pattern,": *");
  96. X    h = pattern + strlen(pattern);
  97. X    interp(h,(sizeof buf) - (h-buf),"%s");
  98. X    h[24] = '\0';        /* compensate for notesfiles */
  99. X    while (*h) {
  100. X        if (index("\\[.^*$'\"",*h) != Nullch)
  101. X        *h++ = '.';
  102. X        else
  103. X        h++;
  104. X    }
  105. X#ifdef DEBUGGING
  106. X    if (debug & DEB_SEARCH_AHEAD) {
  107. X        fputs("(hit CR)",stdout);
  108. X        fflush(stdout);
  109. X        gets(buf+128);
  110. X        printf("\npattern = %s\n",pattern);
  111. X    }
  112. X#endif
  113. X    if ((s = compile(&srchcompex,pattern,TRUE,TRUE)) != Nullch) {
  114. X                    /* compile regular expression */
  115. X        printf("\n%s\n",s);
  116. X        srchahead = 0;
  117. X    }
  118. X    if (srchahead) {
  119. X        srchahead = art;
  120. X        for (;;) {
  121. X        srchahead++;    /* go forward one article */
  122. X        if (srchahead > lastart) { /* out of articles? */
  123. X#ifdef DEBUGGING
  124. X            if (debug)
  125. X            fputs("(not found)",stdout);
  126. X#endif
  127. X            break;
  128. X        }
  129. X        if (!was_read(srchahead) &&
  130. X            wanted(&srchcompex,srchahead,0)) {
  131. X                    /* does the shoe fit? */
  132. X#ifdef DEBUGGING
  133. X            if (debug)
  134. X            printf("(%ld)",(long)srchahead);
  135. X#endif
  136. X#ifdef SERVER
  137. X            nntpopen(srchahead,GET_HEADER);
  138. X#else
  139. X            artopen(srchahead);
  140. X#endif
  141. X            break;
  142. X        }
  143. X        if (input_pending())
  144. X            break;
  145. X        }
  146. X        fflush(stdout);
  147. X    }
  148. X    }
  149. X    else
  150. X#endif
  151. X    {
  152. X    if (art+1 <= lastart)/* how about a pre-fetch? */
  153. X#ifdef SERVER
  154. X        nntpopen(art+1,GET_HEADER);    /* look for the next article */
  155. X#else
  156. X        artopen(art+1);    /* look for the next article */
  157. X#endif
  158. X    }
  159. X}
  160. X#endif
  161. X
  162. X/* see what else we can do while they are reading */
  163. X
  164. Xvoid
  165. Xcollect_subjects()
  166. X{
  167. X#ifdef PENDING
  168. X# ifdef CACHESUBJ
  169. X    ART_NUM oldart = openart;
  170. X    ART_POS oldartpos;
  171. X
  172. X    if (!in_ng || !srchahead)
  173. X    return;
  174. X    if (oldart)            /* remember where we were in art */
  175. X    oldartpos = ftell(artfp);
  176. X    if (srchahead >= subj_to_get)
  177. X    subj_to_get = srchahead+1;
  178. X    while (!input_pending() && subj_to_get <= lastart)
  179. X    fetchsubj(subj_to_get++,FALSE,FALSE);
  180. X    if (oldart) {
  181. X    artopen(oldart);
  182. X    fseek(artfp,oldartpos,0);    /* do not screw the pager */
  183. X    }
  184. X# endif
  185. X#endif
  186. X}
  187. X
  188. END_OF_FILE
  189.   if test 3117 -ne `wc -c <'cheat.c'`; then
  190.     echo shar: \"'cheat.c'\" unpacked with wrong size!
  191.   fi
  192.   # end of 'cheat.c'
  193. fi
  194. if test -f 'common.h' -a "${1}" != "-c" ; then 
  195.   echo shar: Will not clobber existing file \"'common.h'\"
  196. else
  197.   echo shar: Extracting \"'common.h'\" \(28180 characters\)
  198.   sed "s/^X//" >'common.h' <<'END_OF_FILE'
  199. X/* $Header: common.h,v 4.3.3.3 91/01/16 02:00:32 davison Trn $
  200. X * 
  201. X * $Log:    common.h,v $
  202. X * Revision 4.3.3.3  91/01/16  16:28:32  davison
  203. X * integrated rn patches 48-54 and tweaked defines a bit.
  204. X * 
  205. X * Revision 4.3.3.2  90/08/20  16:28:32  davison
  206. X * Tweaked a couple rn's into trn's.
  207. X * 
  208. X * Revision 4.3.3.1  90/07/21  20:15:23  davison
  209. X * Initial Trn Release
  210. X * 
  211. X * Revision 4.3.2.23  90/12/30  23:58:59  sob
  212. X * Fixed CANCELHEADER.
  213. X * 
  214. X * Revision 4.3.2.22  90/12/04  02:51:59  sob
  215. X * removed _SYS_TYPE.H label
  216. X * 
  217. X * Revision 4.3.2.21  90/11/23  10:39:49  sob
  218. X * cleaned up more cruft.
  219. X * 
  220. X * Revision 4.3.2.20  90/11/22  13:18:28  sob
  221. X * Conditionalized TRUE and FALSE to compensate for AIX which has its own
  222. X * definitions.
  223. X * 
  224. X * Revision 4.3.2.19  90/11/09  23:15:12  sob
  225. X * Added sys/stream.h since sys/ptem.h depends on it.
  226. X * 
  227. X * Revision 4.3.2.18  90/11/06  00:11:04  sob
  228. X * Attempt to deal with USG C compilers that can't cope with mutiple
  229. X * includes of the same include file.
  230. X * 
  231. X * Revision 4.3.2.17  90/11/05  23:29:37  sob
  232. X * Added the include of /usr/include/sys/ptem.h if defined in config.h.
  233. X * 
  234. X * Revision 4.3.2.16  90/11/04  03:34:32  sob
  235. X * Changed and moved GETWD to Configure.
  236. X * 
  237. X * Revision 4.3.2.15  90/10/01  02:07:36  sob
  238. X * Increased LBUFLEN from 512 to 1024 per request of ken@csis.dit.csiro.au.
  239. X * 
  240. X * Revision 4.3.2.14  90/10/01  01:56:06  sob
  241. X * Fixed problem with the call to MBOXSAVER reported by news@twwells.com.
  242. X * 
  243. X * Revision 4.3.2.13  90/05/08  22:05:37  sob
  244. X * Added quick startup (-q) flag.
  245. X * 
  246. X * Revision 4.3.2.12  90/04/23  00:32:04  sob
  247. X * More cleanup.
  248. X * 
  249. X * Revision 4.3.2.11  90/04/14  19:37:07  sob
  250. X * Added better support for the NeXT.
  251. X * 
  252. X * Revision 4.3.2.10  90/04/06  20:54:12  sob
  253. X * Corrected forward definition of fseek()
  254. X * 
  255. X * Revision 4.3.2.9  90/03/17  21:19:04  sob
  256. X * Removed the incorrect forward definition of sprintf().
  257. X * 
  258. X * Revision 4.3.2.8  89/12/20  20:40:03  sob
  259. X * Changed ACT_POS from short to long per suggestion from eps@cd.SFSU.EDU.
  260. X * 
  261. X * Revision 4.3.2.7  89/12/08  22:43:12  sob
  262. X * Corrected typo pointed out by weening@gang-of-four.stanford.edu
  263. X * 
  264. X * Revision 4.3.2.6  89/11/28  01:57:31  sob
  265. X * Added initlines_specified variable for use with SIGWINCH support.
  266. X * 
  267. X * Revision 4.3.2.5  89/11/28  00:30:56  sob
  268. X * Reversed the CANCELHEADER definitions.
  269. X * 
  270. X * Revision 4.3.2.4  89/11/27  01:29:23  sob
  271. X * Altered NNTP code per ideas suggested by Bela Lubkin
  272. X * <filbo@gorn.santa-cruz.ca.us>
  273. X * 
  274. X * Revision 4.3.2.3  89/11/26  19:32:06  sob
  275. X * Increased the size of MAXRCLINE from 1000 to 1500
  276. X * Increated HASHSIZ from 1103 to 1693
  277. X * 
  278. X * Revision 4.3.2.2  89/11/07  23:18:49  sob
  279. X * Repaired NEWSHEADER and CANCEL to work correctly with NNTP and INTERNET.
  280. X * 
  281. X * Revision 4.3.2.1  89/11/06  00:12:33  sob
  282. X * Added RRN support from NNTP 1.5
  283. X * 
  284. X * Revision 4.3.1.4  86/10/31  15:46:09  lwall
  285. X * Expanded maximum number of .newsrc lines for net reorganization.
  286. X * 
  287. X * Revision 4.3.1.3  85/05/23  17:19:32  lwall
  288. X * Now allows 'r' and 'f' on null articles.
  289. X * 
  290. X * Revision 4.3.1.2  85/05/13  09:30:39  lwall
  291. X * Added CUSTOMLINES option.
  292. X * 
  293. X * Revision 4.3.1.1  85/05/10  11:32:04  lwall
  294. X * Branch for patches.
  295. X * 
  296. X * Revision 4.3  85/05/01  11:37:11  lwall
  297. X * Baseline for release with 4.3bsd.
  298. X * 
  299. X */
  300. X
  301. X#include <stdio.h>
  302. X#include <sys/types.h>
  303. X#include <sys/stat.h>
  304. X#include <ctype.h>
  305. X#include "config.h"    /* generated by installation script */
  306. X#ifdef WHOAMI
  307. X#    include <whoami.h>
  308. X#endif
  309. X#ifndef isalnum
  310. X#   define isalnum(c) (isalpha(c) || isdigit(c))
  311. X#endif
  312. X
  313. X#include <errno.h>
  314. X#include <signal.h>
  315. X#ifdef IOCTL
  316. X#include <sys/ioctl.h>
  317. X#endif
  318. X
  319. X#ifdef FCNTL
  320. X#   include <fcntl.h>
  321. X#endif
  322. X
  323. X#ifdef TERMIO
  324. X#   include <termio.h>
  325. X#else
  326. X#   include <sgtty.h>
  327. X#endif
  328. X
  329. X#ifdef GETPWENT
  330. X#   include <pwd.h>
  331. X#endif
  332. X
  333. X#ifdef PTEM
  334. X#include <sys/stream.h>
  335. X#include <sys/ptem.h>
  336. X#endif
  337. X
  338. X#define BITSPERBYTE 8
  339. X#ifdef pdp11
  340. X#define LBUFLEN 512    /* line buffer length */
  341. X#else
  342. X#define LBUFLEN 1024    /* line buffer length */
  343. X#endif
  344. X            /* (don't worry, .newsrc lines can exceed this) */
  345. X#ifdef pdp11
  346. X#   define CBUFLEN 256    /* command buffer length */
  347. X#   define PUSHSIZE 128
  348. X#else
  349. X#   define CBUFLEN 512    /* command buffer length */
  350. X#   define PUSHSIZE 256
  351. X#endif
  352. X#ifdef pdp11
  353. X#   define MAXFILENAME 128
  354. X#else
  355. X#   define MAXFILENAME 512
  356. X#endif
  357. X#define LONGKEY 15    /* longest keyword: currently "posting-version" */
  358. X#define FINISHCMD 0177
  359. X
  360. X/* some handy defs */
  361. X
  362. X#define bool char
  363. X#ifndef TRUE
  364. X#define TRUE (1)
  365. X#endif
  366. X#ifndef FALSE
  367. X#define FALSE (0)
  368. X#endif
  369. X#define Null(t) ((t)0)
  370. X#define Nullch Null(char *)
  371. X#define Nullfp Null(FILE *)
  372. X
  373. X#define Ctl(ch) (ch & 037)
  374. X
  375. X#define strNE(s1,s2) (strcmp(s1,s2))
  376. X#define strEQ(s1,s2) (!strcmp(s1,s2))
  377. X#define strnNE(s1,s2,l) (strncmp(s1,s2,l))
  378. X#define strnEQ(s1,s2,l) (!strncmp(s1,s2,l))
  379. X
  380. X/* Things we can figure out ourselves */
  381. X
  382. X#ifdef SIGTSTP
  383. X#   define BERKELEY     /* include job control signals? */
  384. X#endif
  385. X
  386. X#ifdef FIONREAD
  387. X#   define PENDING
  388. X#else
  389. X#   ifdef O_NDELAY
  390. X#    define PENDING
  391. X#   endif
  392. X#endif
  393. X
  394. X#ifdef EUNICE
  395. X#   define LINKART        /* add 1 level of possible indirection */
  396. X#   define UNLINK(victim) while (!unlink(victim))
  397. X#else
  398. X#   define UNLINK(victim) unlink(victim)
  399. X#endif
  400. X
  401. X/* Valid substitutions for strings marked with % comment are:
  402. X *    %a    Current article number
  403. X *    %A    Full name of current article (%P/%c/%a)
  404. X *        (if LINKART defined, is the name of the real article)
  405. X *    %b    Destination of a save command, a mailbox or command
  406. X *    %B    The byte offset to the beginning of the article for saves
  407. X *        with or without the header
  408. X *    %c    Current newsgroup, directory form
  409. X *    %C    Current newsgroup, dot form
  410. X *    %d    %P/%c
  411. X *    %D    Old Distribution: line
  412. X *    %f    Old From: line or Reply-To: line
  413. X *    %F    Newsgroups to followup to from Newsgroups: and Followup-To:
  414. X *    %h    Name of header file to pass to mail or news poster
  415. X *    %H    Host name (yours)
  416. X *    %i    Old Message-I.D.: line, with <>
  417. X *    %I    Inclusion indicator
  418. X *    %l    News administrator login name
  419. X *    %L    Login name (yours)
  420. X *    %M    Number of articles markd with M
  421. X *    %n    Newsgroups from source article
  422. X *    %N    Full name (yours)
  423. X *    %o    Organization (yours)
  424. X *    %O    Original working directory (where you ran rn from)
  425. X *    %p    Your private news directory (-d switch)
  426. X *    %P    Public news spool directory (SPOOLDIR)
  427. X *    %r    Last reference (parent article id)
  428. X *    %R    New references list
  429. X *    %s    Subject, with all Re's and (nf)'s stripped off
  430. X *    %S    Subject, with one Re stripped off
  431. X *    %t    New To: line derived from From: and Reply-To (Internet always)
  432. X *    %T    New To: line derived from Path:
  433. X *    %u    Number of unread articles
  434. X *    %U    Number of unread articles disregarding current article
  435. X *    %x    News library directory, usually /usr/lib/news
  436. X *    %X    Rn library directory, usually %x/rn
  437. X *    %z    Size of current article in bytes.
  438. X *    %~    Home directory
  439. X *    %.    Directory containing . files
  440. X *    %$    current process number
  441. X *    %{name} Environment variable "name".  %{name-default} form allowed.
  442. X *    %[name]    Header line beginning with "Name: ", without "Name: " 
  443. X *    %"prompt"
  444. X *        Print prompt and insert what is typed.
  445. X *    %`command`
  446. X *        Insert output of command.
  447. X *    %(test_text=pattern?if_text:else_text)
  448. X *        Substitute if_text if test_text matches pattern, otherwise
  449. X *        substitute else_text.  Use != for negated match.
  450. X *        % substitutions are done on test_text, if_text, and else_text.
  451. X *        (Note: %() only works if CONDSUB defined.)
  452. X *    %digit    Substitute the text matched by the nth bracket in the last
  453. X *        pattern that had brackets.  %0 matches the last bracket
  454. X *        matched, in case you had alternatives.
  455. X *
  456. X *    Put ^ in the middle to capitalize the first letter: %^C = Net.jokes
  457. X *    Put _ in the middle to capitalize last component: %_c = net/Jokes
  458. X *
  459. X *    ~ interpretation in filename expansion happens after % expansion, so
  460. X *    you could put ~%{NEWSLOGNAME-news} and it will expand correctly.
  461. X */
  462. X
  463. X/* *** System Dependent Stuff *** */
  464. X
  465. X/* NOTE: many of these are defined in the config.h file */
  466. X
  467. X/* name of organization */
  468. X#ifndef ORGNAME
  469. X#   define ORGNAME "ACME Widget Company, Widget Falls, Southern North Dakota"
  470. X#endif
  471. X
  472. X#ifndef MBOXCHAR
  473. X#   define MBOXCHAR 'F'    /* how to recognize a mailbox by 1st char */
  474. X#endif
  475. X
  476. X#ifndef ROOTID
  477. X#   define ROOTID 0        /* uid of superuser */
  478. X#endif
  479. X
  480. X#ifdef NORMSIG
  481. X#   define sigset signal
  482. X#   define sigignore(sig) signal(sig,SIG_IGN)
  483. X#endif
  484. X
  485. X#ifndef LOGDIRFIELD
  486. X#   define LOGDIRFIELD 6        /* Which field (origin 1) is the */
  487. X                    /* login directory in /etc/passwd? */
  488. X                    /* (If it is not kept in passwd, */
  489. X                    /* but getpwnam() returns it, */
  490. X                    /* define the symbol GETPWENT) */
  491. X#endif
  492. X#ifndef GCOSFIELD
  493. X#   define GCOSFIELD 5
  494. X#endif
  495. X
  496. X#ifndef NEGCHAR
  497. X#   define NEGCHAR '!'
  498. X#endif
  499. X
  500. X/* Space conservation section */
  501. X
  502. X/* To save D space, cut down size of MAXRCLINE, NGMAX, VARYSIZE. */
  503. X#define MAXRCLINE 1500    /* number of lines allowed in .newsrc */
  504. X            /* several parallel arrays affected. */
  505. X            /* (You can have more lines in the active file, */
  506. X            /* just not in the .newsrc) */
  507. X#define HASHSIZ 1693    /* should be prime, and at least MAXRCLINE + 10% */
  508. X#define NGMAX 100    /* number of newsgroups allowed on command line */
  509. X            /* undefine ONLY symbol to disable "only" feature */
  510. X#define VARYSIZE 256    /* this makes a block 1024 bytes long in DECville */
  511. X            /* (used by virtual array routines) */
  512. X
  513. X/* Undefine any of the following features to save both I and D space */
  514. X/* In general, earlier ones are easier to get along without */
  515. X/* Pdp11's without split I and D may have to undefine them all */
  516. X#define DEBUGGING    /* include debugging code */
  517. X#define USETHREADS    /* Add article-thread following */
  518. X#define CUSTOMLINES    /* include code for HIDELINE and PAGESTOP */
  519. X#define PUSHBACK    /* macros and keymaps using pushback buffer */
  520. X#define SPEEDOVERMEM    /* use more memory to run faster */
  521. X#define WORDERASE    /* enable ^W to erase a word */
  522. X#define MAILCALL    /* check periodically for mail */
  523. X#define CLEAREOL    /* use clear to end-of-line instead of clear screen */
  524. X#define NOFIREWORKS    /* keep whole screen from flashing on certain */
  525. X            /* terminals such as older Televideos */
  526. X#define VERIFY        /* echo the command they just typed */
  527. X#define HASHNG        /* hash newsgroup lines for fast lookup-- */
  528. X            /* linear search used if not defined */
  529. X#define CONDSUB        /* allow %(cond?text:text) */
  530. X#define BACKTICK    /* allow %`command` */
  531. X#define PROMPTTTY    /* allow %"prompt" */
  532. X#define ULSMARTS    /* catch _^H in text and do underlining */
  533. X#define TERMMOD        /* allow terminal type modifier on switches */
  534. X#define BAUDMOD        /* allow baudrate modifier on switches */
  535. X#define GETLOGIN    /* use getlogin() routine as backup to environment */
  536. X            /* variables USER or LOGNAME */
  537. X#define ORGFILE        /* if organization begins with /, look up in file */
  538. X#define TILDENAME    /* allow ~logname expansion */
  539. X#define SETENV        /* allow command line environment variable setting */
  540. X#define MAKEDIR        /* use our makedir() instead of shell script */
  541. X#define MEMHELP        /* keep help messages in memory */
  542. X#define VERBOSE        /* compile in more informative messages */
  543. X#define TERSE        /* compile in shorter messages */
  544. X            /* (Note: both VERBOSE and TERSE can be defined; -t
  545. X             * sets terse mode.  One or the other MUST be defined.
  546. X             */
  547. X#ifndef pdp11
  548. X#   define CACHESUBJ    /* cache subject lines in memory */
  549. X            /* without this ^N still works but runs really slow */
  550. X            /* but you save lots and lots of D space */
  551. X#   define CACHEFIRST    /* keep absolute first article numbers in memory */
  552. X            /* cost: about 2k */
  553. X#endif
  554. X#define ROTATION    /* enable x, X and ^X commands to work */
  555. X#define DELBOGUS    /* ask if bogus newsgroups should be deleted */
  556. X#define RELOCATE    /* allow newsgroup rearranging */
  557. X#define ESCSUBS        /* escape substitutions in multi-character commands */
  558. X#define DELAYMARK    /* allow articles to be temporarily marked as read */
  559. X            /* until exit from current newsgroup or Y command */
  560. X#define MCHASE        /* unmark xrefed articles on m or M */
  561. X#define MUNGHEADER    /* allow alternate header formatting via */
  562. X            /* environment variable ALTHEADER (not impl) */
  563. X#define ASYNC_PARSE    /* allow parsing headers asyncronously to reading */
  564. X            /* used by MCHASE and MUNGHEADER */
  565. X#define FINDNEWNG    /* check for new newsgroups on startup */
  566. X#define FASTNEW        /* do optimizations on FINDNEWNG for faster startup */
  567. X            /* (this optimization can make occasional mistakes */
  568. X            /* if a group is removed and another group of the */
  569. X            /* same length is added, and if no softpointers are */
  570. X            /* affected by said change.) */
  571. X#define INNERSEARCH    /* search command 'g' with article */
  572. X#define CATCHUP        /* catchup command at newsgroup level */
  573. X#define NGSEARCH    /* newsgroup pattern matching */
  574. X#define ONLY        /* newsgroup restrictions by pattern */
  575. X#define KILLFILES    /* automatic article killer files */
  576. X#define ARTSEARCH    /* pattern searches among articles */
  577. X            /* /, ?, ^N, ^P, k, K */
  578. X
  579. X/* some dependencies among options */
  580. X
  581. X#ifndef ARTSEARCH
  582. X#   undef KILLFILES
  583. X#   undef INNERSEARCH
  584. X#   undef CACHESUBJ
  585. X#endif
  586. X
  587. X#ifndef DELAYMARK
  588. X#   ifndef MCHASE
  589. X#    ifndef MUNGHEADER
  590. X#        undef ASYNC_PARSE
  591. X#    endif
  592. X#   endif
  593. X#endif
  594. X
  595. X#ifndef SETUIDGID
  596. X#   define eaccess access
  597. X#endif
  598. X
  599. X#ifdef ONLY                /* idiot lint doesn't grok #if */
  600. X#   define NGSORONLY
  601. X#else
  602. X#   ifdef NGSEARCH
  603. X#    define NGSORONLY
  604. X#   endif
  605. X#endif
  606. X
  607. X#ifdef VERBOSE
  608. X#   ifdef TERSE
  609. X#    define IF(c) if (c)
  610. X#    define ELSE else
  611. X#   else
  612. X#    define IF(c)
  613. X#    define ELSE
  614. X#   endif
  615. X#else /* !VERBOSE */
  616. X#   ifndef TERSE
  617. X#    define TERSE
  618. X#   endif
  619. X#   define IF(c) "IF" outside of VERBOSE???
  620. X#   define ELSE "ELSE" outside of VERBOSE???
  621. X#endif
  622. X
  623. X#ifdef DEBUGGING
  624. X#   define assert(ex) {if (!(ex)){fprintf(stderr,"Assertion failed: file %s, line %d\n", __FILE__, __LINE__);sig_catcher(0);}}
  625. X#else
  626. X#   define assert(ex) ;
  627. X#endif
  628. X
  629. X#ifdef SPEEDOVERMEM
  630. X#   define OFFSET(x) (x)
  631. X#else
  632. X#   define OFFSET(x) ((x)-absfirst)
  633. X#endif
  634. X
  635. X/* If you're strapped for space use the help messages in shell scripts */
  636. X/* if {NG,ART,PAGER,SUBS}HELP is undefined, help messages are in memory */
  637. X#ifdef MEMHELP  /* undef MEMHELP above to get them all as sh scripts */
  638. X#   undef NGHELP
  639. X#   undef ARTHELP
  640. X#   undef PAGERHELP
  641. X#   undef SUBSHELP
  642. X#else
  643. X#   ifndef NGHELP            /* % and ~ */
  644. X#    define NGHELP "%X/ng.help"
  645. X#   endif
  646. X#   ifndef ARTHELP            /* % and ~ */
  647. X#    define ARTHELP "%X/art.help"
  648. X#   endif
  649. X#   ifndef PAGERHELP        /* % and ~ */
  650. X#    define PAGERHELP "%X/pager.help"
  651. X#   endif
  652. X#   ifndef SUBSHELP        /* % and ~ */
  653. X#    define SUBSHELP "%X/subs.help"
  654. X#   endif
  655. X#endif
  656. X
  657. X#ifdef CLEAREOL
  658. X#   define TCSIZE 512    /* capacity for termcap strings */
  659. X#else
  660. X#   ifdef pdp11
  661. X#    define TCSIZE 256    /* capacity for termcap strings */
  662. X#   else
  663. X#    define TCSIZE 512    /* capacity for termcap srings */
  664. X#   endif
  665. X#endif
  666. X
  667. X/* Additional ideas:
  668. X *    Make the do_newsgroup() routine a separate process.
  669. X *    Keep .newsrc on disk instead of in memory.
  670. X *    Overlays, if you have them.
  671. X *    Get a bigger machine.
  672. X */
  673. X
  674. X/* End of Space Conservation Section */
  675. X
  676. X/* More System Dependencies */
  677. X
  678. X/* news library */
  679. X#ifndef LIB        /* ~ and %l only ("~%l" is permissable) */
  680. X#   define LIB "/usr/lib/news"
  681. X#endif
  682. X
  683. X/* path to private executables */
  684. X#ifndef RNLIB        /* ~, %x and %l only */
  685. X#   define RNLIB "%x/trn"
  686. X#endif
  687. X
  688. X/* system-wide RNINIT switches */
  689. X#ifndef GLOBINIT
  690. X#   define GLOBINIT "%X/INIT"
  691. X#endif
  692. X
  693. X/* where to find news files */
  694. X#ifndef SPOOL            /* % and ~ */
  695. X#   define SPOOL "/usr/spool/news"
  696. X#endif
  697. X
  698. X#ifdef USETHREADS
  699. X# ifdef THREAD_DIR
  700. X#   ifdef LONG_THREAD_NAMES
  701. X#    undef SUFFIX
  702. X#   else
  703. X#     ifndef SUFFIX
  704. X#    define SUFFIX ".th"
  705. X#     endif
  706. X#   endif
  707. X# else
  708. X#   define THREAD_DIR    SPOOL
  709. X#   ifndef SUFFIX
  710. X#     define SUFFIX    "/.thread"
  711. X#   endif
  712. X#   undef LONG_THREAD_NAMES
  713. X# endif
  714. X# ifndef NEW_THREAD
  715. X#   define NEW_THREAD ".new"
  716. X# endif
  717. X#endif
  718. X
  719. X/* default characters to use in the selection menu */
  720. X#ifndef SELECTCHARS
  721. X#   define SELECTCHARS "abcdefgijlorstuvwxz1234567890"
  722. X#endif
  723. X
  724. X/* file containing list of active newsgroups and max article numbers */
  725. X#ifndef ACTIVE            /* % and ~ */
  726. X#   define ACTIVE "%x/active"
  727. X#endif
  728. X#ifdef SERVER
  729. X#   ifndef ACTIVE1
  730. X#    define ACTIVE1 "%X/active1"
  731. X#   endif
  732. X#endif
  733. X#ifndef ACTIVE2
  734. X#   define ACTIVE2 "%X/active2"
  735. X#endif
  736. X
  737. X/* location of history file */
  738. X#ifndef ARTFILE            /* % and ~ */
  739. X#    define ARTFILE "%x/history"
  740. X#endif
  741. X
  742. X/* command to setup a new .newsrc */
  743. X#ifndef NEWSETUP        /* % and ~ */
  744. X#   define NEWSETUP "newsetup"
  745. X#endif
  746. X
  747. X/* command to display a list of un-subscribed-to newsgroups */
  748. X#ifndef NEWSGROUPS        /* % and ~ */
  749. X#   define NEWSGROUPS "newsgroups"
  750. X#endif
  751. X
  752. X/* preferred shell for use in doshell routine */
  753. X/*  ksh or sh would be okay here */
  754. X#ifndef PREFSHELL
  755. X#   define PREFSHELL "/bin/csh"
  756. X#endif
  757. X
  758. X/* path to fastest starting shell */
  759. X#ifndef SH
  760. X#   define SH "/bin/sh"
  761. X#endif
  762. X
  763. X/* default unshar'ing program */
  764. X#ifndef UNSHAR
  765. X#   define UNSHAR "/bin/sh"
  766. X#endif
  767. X
  768. X/* path to default editor */
  769. X#ifndef DEFEDITOR
  770. X#   define DEFEDITOR "/usr/ucb/vi"
  771. X#endif
  772. X
  773. X/* location of macro file */
  774. X#ifndef RNMACRO
  775. X#   ifdef PUSHBACK
  776. X#    define RNMACRO "%./.rnmac"
  777. X#   endif
  778. X#endif
  779. X
  780. X/* location of full name */
  781. X#ifndef FULLNAMEFILE
  782. X#   ifndef PASSNAMES
  783. X#    define FULLNAMEFILE "%./.fullname"
  784. X#   endif
  785. X#endif
  786. X
  787. X/* virtual array file name template */
  788. X#ifndef VARYNAME        /* % and ~ */
  789. X#   define VARYNAME "/tmp/rnvary.%$"
  790. X#endif
  791. X
  792. X/* where to compile a new newsgroup list */
  793. X#ifndef RNEWNAME
  794. X#   define RNEWNAME "/tmp/rnew.%$"
  795. X#endif
  796. X
  797. X/* file to pass header to followup article poster */
  798. X#ifndef HEADNAME        /* % and ~ */
  799. X#   define HEADNAME "%./.rnhead"
  800. X/* or alternately #define HEADNAME "/tmp/rnhead.%$" */
  801. X#endif
  802. X
  803. X#ifndef MAKEDIR
  804. X/* shell script to make n-deep subdirectories */
  805. X#   ifndef DIRMAKER        /* % and ~ */
  806. X#    define DIRMAKER "%X/makedir"
  807. X#   endif
  808. X#endif
  809. X
  810. X/* location of newsrc file */
  811. X#ifndef RCNAME        /* % and ~ */
  812. X#   define RCNAME "%./.newsrc"
  813. X#endif
  814. X
  815. X/* temporary newsrc file in case we crash while writing out */
  816. X#ifndef RCTNAME        /* % and ~ */
  817. X#   define RCTNAME "%./.newnewsrc"
  818. X#endif
  819. X
  820. X/* newsrc file at the beginning of this session */
  821. X#ifndef RCBNAME        /* % and ~ */
  822. X#   define RCBNAME "%./.oldnewsrc"
  823. X#endif
  824. X
  825. X/* if existent, contains process number of current or crashed rn */
  826. X#ifndef LOCKNAME        /* % and ~ */
  827. X#   define LOCKNAME "%./.rnlock"
  828. X#endif
  829. X
  830. X/* information from last invocation of rn */
  831. X#ifndef LASTNAME        /* % and ~ */
  832. X#   define LASTNAME "%./.rnlast"
  833. X#endif
  834. X
  835. X/* file with soft pointers into the active file */
  836. X#ifndef SOFTNAME        /* % and ~ */
  837. X#   define SOFTNAME "%./.rnsoft"
  838. X#endif
  839. X
  840. X/* list of article numbers to mark as unread later (see M and Y cmmands) */
  841. X#ifndef RNDELNAME        /* % and ~ */
  842. X#   define RNDELNAME "%./.rndelay"
  843. X#endif
  844. X
  845. X/* a motd-like file for rn */
  846. X#ifndef NEWSNEWSNAME        /* % and ~ */
  847. X#   define NEWSNEWSNAME "%X/newsnews"
  848. X#endif
  849. X
  850. X/* command to send a reply */
  851. X#ifndef MAILPOSTER        /* % and ~ */
  852. X#   define MAILPOSTER "Rnmail -h %h"
  853. X#endif
  854. X
  855. X#ifdef INTERNET
  856. X#   ifndef MAILHEADER        /* % */
  857. X#    ifdef CONDSUB
  858. X#        define MAILHEADER "To: %t\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\n%(%[references]!=^$?References\\: %[references]\n)Organization: %o\nCc: \nBcc: \n\n"
  859. X#    else
  860. X#        define MAILHEADER "To: %t\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\nReferences: %[references]\nCc: \nBcc: \n\n"
  861. X#    endif
  862. X#   endif
  863. X#else
  864. X#   ifndef MAILHEADER        /* % */
  865. X#    ifdef CONDSUB
  866. X#        define MAILHEADER "To: %T\nSubject: %(%i=^$?:Re: %S\nNewsgroups: %n\nIn-Reply-To: %i)\n%(%[references]!=^$?References\\: %[references]\n)Organization: %o\nCc: \nBcc: \n\n"
  867. X#    else
  868. X#        define MAILHEADER "To: %T\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\nReferences: %[references]\nCc: \nBcc: \n\n"
  869. X#    endif
  870. X#   endif
  871. X#endif
  872. X
  873. X#ifndef YOUSAID            /* % */
  874. X#   define YOUSAID "In article %i you write:"
  875. X#endif
  876. X
  877. X/* command to submit a followup article */
  878. X#ifndef NEWSPOSTER        /* % and ~ */
  879. X#   define NEWSPOSTER "Pnews -h %h"
  880. X#endif
  881. X
  882. X#ifndef NEWSHEADER        /* % */
  883. X#   ifdef CONDSUB
  884. X#ifdef INTERNET
  885. X#    define NEWSHEADER "Newsgroups: %(%F=^$?%C:%F)\nSubject: %(%S=^$?%\"\n\nSubject: \":Re: %S)\nSummary: \nExpires: \n%(%R=^$?:References: %R\n)Sender: \nFollowup-To: \nDistribution: %(%i=^$?%\"Distribution: \":%D)\nOrganization: %o\nKeywords: %[keywords]\n\n"
  886. X#else
  887. X#    define NEWSHEADER "Newsgroups: %(%F=^$?%C:%F)\nSubject: %(%S=^$?%\"\n\nSubject: \":Re: %S)\nSummary: \nExpires: \n%(%R=^$?:References: %R\n)Sender: \nFollowup-To: \nDistribution: %(%i=^$?%\"Distribution: \":%D)\nOrganization: %o\nKeywords: %[keywords]\n\n"
  888. X#endif
  889. X#   else
  890. X#    ifdef INTERNET
  891. X#        define NEWSHEADER "Newsgroups: %F\nSubject: Re: %S\nSummary: \nExpires: \nReferences: %R\nSender: \nFollowup-To: \nDistribution: %D\nOrganization: %o\nKeywords: %[keywords]\n\n"
  892. X#    else
  893. X#        define NEWSHEADER "Newsgroups: %F\nSubject: Re: %S\nSummary: \nExpires: \nReferences: %R\nSender: \nFollowup-To: \nDistribution: %D\nOrganization: %o\nKeywords: %[keywords]\n\n"
  894. X#    endif
  895. X#   endif
  896. X#endif
  897. X
  898. X#ifndef ATTRIBUTION        /* % */
  899. X#   define ATTRIBUTION "In article %i %f writes:"
  900. X#endif
  901. X
  902. X#ifndef PIPESAVER        /* % */
  903. X#   ifdef CONDSUB
  904. X#       ifdef SERVER
  905. X#               define PIPESAVER "%(%B=^0$?<%P/rrn%a.%$:tail +%Bc %P/rrn%a.%$ |) %b"
  906. X#       else
  907. X#        define PIPESAVER "%(%B=^0$?<%A:tail +%Bc %A |) %b"
  908. X#    endif
  909. X#   else
  910. X#       ifdef SERVER
  911. X#               define PIPESAVER "tail +%Bc %P/rrn%a.%$ | %b"
  912. X#       else
  913. X#        define PIPESAVER "tail +%Bc %A | %b"
  914. X#    endif
  915. X#   endif
  916. X#endif
  917. X
  918. X#ifndef EXSAVER
  919. X#    ifdef SERVER
  920. X#    define EXSAVER "tail +%Bc %P/rrn%a.%$ | %e"
  921. X#    else
  922. X#    define EXSAVER "tail +%Bc %A | %e"
  923. X#    endif
  924. X#endif
  925. X
  926. X#ifndef NORMSAVER        /* % and ~ */
  927. X#    ifdef SERVER
  928. X#    define NORMSAVER "%X/norm.saver %P/rrn%a.%$ %P %c %a %B %C \"%b\""
  929. X#    else
  930. X#       define NORMSAVER "%X/norm.saver %A %P %c %a %B %C \"%b\""
  931. X#    endif
  932. X#endif
  933. X
  934. X#ifndef MBOXSAVER        /* % and ~ */
  935. X#   ifdef MININACT        /* 2.10.2 site? */
  936. X#       ifdef SERVER
  937. X#           define MBOXSAVER "%X/mbox.saver %P/rrn%a.%$ %P %c %a %B %C \"%b\" \"From %T %`date`\""
  938. X#       else
  939. X#        define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %T %`date`\""
  940. X#    endif
  941. X#   else
  942. X#    ifdef CONDSUB
  943. X#           ifdef SERVER
  944. X#               define MBOXSAVER "%X/mbox.saver %P/rrn%a.%$ %P %c %a %B %C \"%b\" \"From %T %(%[date]=^\\(\\w*\\), \\(\\w*\\)-\\(\\w*\\)-\\(\\w*\\) \\([^ ]*\\)?%1 %3 %(%2=..?%2: %2) %5 19%4)\""
  945. X#           else
  946. X#            define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %T %(%[date]=^\\(\\w*\\), \\(\\w*\\)-\\(\\w*\\)-\\(\\w*\\) \\([^ ]*\\)?%1 %3 %(%2=..?%2: %2) %5 19%4)\""
  947. X#        endif
  948. X                    /* header munging with a vengeance */
  949. X#    else
  950. X#           ifdef SERVER
  951. X#               define MBOXSAVER "%X/mbox.saver %P/rrn%a.%$ %P %c %a %B %C \"%b\" \"From %T %[posted]\""
  952. X#           else
  953. X#            define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %T %[posted]\""
  954. X#        endif
  955. X#    endif
  956. X#   endif
  957. X#endif
  958. X
  959. X#ifdef MKDIRS
  960. X
  961. X#   ifndef SAVEDIR            /* % and ~ */
  962. X#    define SAVEDIR "%p/%c"
  963. X#   endif
  964. X#   ifndef SAVENAME        /* % */
  965. X#    define SAVENAME "%a"
  966. X#   endif
  967. X
  968. X#else
  969. X
  970. X#   ifndef SAVEDIR            /* % and ~ */
  971. X#    define SAVEDIR "%p"
  972. X#   endif
  973. X#   ifndef SAVENAME        /* % */
  974. X#    define SAVENAME "%^C"
  975. X#   endif
  976. X
  977. X#endif
  978. X
  979. X#ifndef KILLGLOBAL        /* % and ~ */
  980. X#   define KILLGLOBAL "%p/KILL"
  981. X#endif
  982. X
  983. X#ifndef KILLLOCAL        /* % and ~ */
  984. X#   define KILLLOCAL "%p/%c/KILL"
  985. X#endif
  986. X
  987. X/* how to cancel an article */
  988. X#ifndef CANCEL
  989. X#   ifdef MININACT            /* 2.10.2 ? */
  990. X#    define CANCEL "%x/inews -h < %h"
  991. X#   else
  992. X#    define CANCEL "inews -h < %h"
  993. X#   endif
  994. X#endif
  995. X
  996. X/* how to cancel an article, continued */
  997. X#ifndef CANCELHEADER
  998. X#ifdef INTERNET
  999. X#   define CANCELHEADER "From: %L@%H (%N)\nNewsgroups: %n\nSubject: cmsg cancel %i\nReferences: %R\nDistribution: %D\nOrganization: %o\n\nThis message was cancelled from within trn.\n"
  1000. X#else
  1001. X#   define CANCELHEADER "From:%L@%H.UUCP (%N)\nNewsgroups: %n\nSubject: cmsg cancel %i\nReferences: %R\nDistribution: %D\nOrganization: %o\n"
  1002. X#endif
  1003. X#endif
  1004. X
  1005. X/* where to find the mail file */
  1006. X#ifndef MAILFILE
  1007. X#   define MAILFILE "/usr/spool/mail/%L"
  1008. X#endif
  1009. X
  1010. X/* how to open binary format files */
  1011. X#ifndef FOPEN_RB
  1012. X#   define FOPEN_RB "r"
  1013. X#endif
  1014. X#ifndef FOPEN_WB
  1015. X#   define FOPEN_WB "w"
  1016. X#endif
  1017. X
  1018. X/* what to do with ansi prototypes -- '()' == ignore, 'x' == use */
  1019. X#ifndef ANSI
  1020. X#   define ANSI(x) ()
  1021. X#endif
  1022. X
  1023. X/* how many characters is a newline in a text file? */
  1024. X#ifndef NL_SIZE
  1025. X#   define NL_SIZE 1
  1026. X#endif
  1027. X
  1028. X/* some important types */
  1029. X
  1030. Xtypedef int        NG_NUM;        /* newsgroup number */
  1031. Xtypedef long        ART_NUM;    /* article number */
  1032. X#ifdef pdp11
  1033. X    typedef short    ART_UNREAD;    /* ordinarily this should be long */
  1034. X                    /* like ART_NUM, but assuming that */
  1035. X                    /* we stay less than 32767 articles */
  1036. X                    /* behind saves a lot of space. */
  1037. X                    /* NOTE: do not make unsigned. */
  1038. X#else
  1039. X    typedef long    ART_UNREAD;
  1040. X#endif
  1041. X#ifdef SERVER
  1042. Xtypedef int        ART_PART;    /* for passing to nntpopen() */
  1043. X#endif
  1044. Xtypedef long        ART_POS;    /* char position in article file */
  1045. Xtypedef int        ART_LINE;    /* line position in article file */
  1046. Xtypedef long        ACT_POS;    /* char position in active file */
  1047. Xtypedef unsigned int    MEM_SIZE;    /* for passing to malloc */
  1048. X
  1049. X
  1050. X/* *** end of the machine dependent stuff *** */
  1051. X
  1052. X/* GLOBAL THINGS */
  1053. X
  1054. X/* file statistics area */
  1055. X
  1056. XEXT struct stat filestat;
  1057. X
  1058. X/* various things of type char */
  1059. X
  1060. Xchar    *index();
  1061. Xchar    *rindex();
  1062. Xchar    *getenv();
  1063. Xchar    *strcat();
  1064. Xchar    *strcpy();
  1065. X
  1066. XEXT char buf[LBUFLEN+1];    /* general purpose line buffer */
  1067. XEXT char cmd_buf[CBUFLEN];    /* buffer for formatting system commands */
  1068. X
  1069. XEXT char *indstr INIT(">");    /* indent for old article embedded in followup */
  1070. X
  1071. XEXT char *cwd INIT(Nullch);        /* current working directory */
  1072. XEXT char *dfltcmd INIT(Nullch);    /* 1st char is default command */
  1073. X
  1074. X/* switches */
  1075. X
  1076. X#ifdef DEBUGGING
  1077. X    EXT int debug INIT(0);                /* -D */
  1078. X#   define DEB_INNERSRCH 32 
  1079. X#   define DEB_FILEXP 64 
  1080. X#   define DEB_HASH 128
  1081. X#   define DEB_XREF_MARKER 256
  1082. X#   define DEB_CTLAREA_BITMAP 512
  1083. X#   define DEB_SOFT_POINTERS 1024
  1084. X#   define DEB_NEWSRC_LINE 2048
  1085. X#   define DEB_SEARCH_AHEAD 4096
  1086. X#   define DEB_CHECKPOINTING 8192
  1087. X#   define DEB_FEED_XREF 16384
  1088. X#endif
  1089. X
  1090. X#ifdef ARTSEARCH
  1091. X    EXT int scanon INIT(0);                /* -S */
  1092. X#endif
  1093. X
  1094. X#ifdef USETHREADS
  1095. X    EXT bool use_threads INIT(THREAD_INIT);        /* -x */
  1096. X    EXT int max_tree_lines INIT(6);
  1097. X    EXT char select_order[4] INIT("lsm");
  1098. X    EXT int select_on INIT(SELECT_INIT);        /* -X */
  1099. X    EXT char end_select INIT('Z');
  1100. X    EXT char page_select INIT('>');
  1101. X#endif
  1102. X
  1103. XEXT bool mbox_always INIT(FALSE);            /* -M */
  1104. XEXT bool norm_always INIT(FALSE);            /* -N */
  1105. XEXT bool checkflag INIT(FALSE);            /* -c */
  1106. XEXT bool suppress_cn INIT(FALSE);            /* -s */
  1107. XEXT int countdown INIT(5);    /* how many lines to list before invoking -s */
  1108. XEXT bool muck_up_clear INIT(FALSE);            /* -loco */
  1109. XEXT bool erase_screen INIT(FALSE);            /* -e */
  1110. X#if defined(CLEAREOL) || defined(USETHREADS)
  1111. XEXT bool can_home INIT(FALSE);
  1112. X#endif
  1113. X#ifdef CLEAREOL
  1114. XEXT bool can_home_clear INIT(FALSE);        /* fancy -e */
  1115. X#endif
  1116. XEXT bool findlast INIT(FALSE);            /* -r */
  1117. XEXT bool typeahead INIT(FALSE);            /* -T */
  1118. X#ifdef VERBOSE
  1119. X#   ifdef TERSE
  1120. X    EXT bool verbose INIT(TRUE);            /* +t */
  1121. X#   endif
  1122. X#endif
  1123. X#ifdef VERIFY
  1124. X    EXT bool verify INIT(FALSE);            /* -v */
  1125. X#endif
  1126. X    EXT bool quickstart INIT(FALSE);            /* -q */
  1127. X
  1128. X#define NOMARKING 0
  1129. X#define STANDOUT 1
  1130. X#define UNDERLINE 2
  1131. XEXT int marking INIT(NOMARKING);            /* -m */
  1132. X
  1133. XEXT ART_LINE initlines INIT(0);        /* -i */
  1134. XEXT bool initlines_specified INIT(FALSE);
  1135. X
  1136. X/* miscellania */
  1137. X
  1138. Xint fseek();
  1139. Xlong atol(), ftell();
  1140. XEXT bool in_ng INIT(FALSE);        /* current state of rn */
  1141. XEXT char mode INIT('i');        /* current state of rn */
  1142. X
  1143. XEXT FILE *tmpfp INIT(Nullfp);    /* scratch fp used for .rnlock, .rnlast, etc. */
  1144. X
  1145. XEXT NG_NUM nextrcline INIT(0);    /* 1st unused slot in rcline array */
  1146. X            /* startup to avoid checking twice in a row */
  1147. X
  1148. Xextern errno;
  1149. X
  1150. X/* Factored strings */
  1151. X
  1152. XEXT char nullstr[] INIT("");
  1153. XEXT char sh[] INIT(SH);
  1154. XEXT char defeditor[] INIT(DEFEDITOR);
  1155. XEXT char hforhelp[] INIT("Type h for help.\n");
  1156. X#ifdef STRICTCR
  1157. XEXT char badcr[] INIT("\nUnnecessary CR ignored.\n");
  1158. X#endif
  1159. XEXT char readerr[] INIT("rn read error");
  1160. XEXT char unsubto[] INIT("\n\nUnsubscribed to newsgroup %s\n");
  1161. XEXT char cantopen[] INIT("Can't open %s\n");
  1162. XEXT char cantcreate[] INIT("Can't create %s\n");
  1163. X
  1164. X#ifdef VERBOSE
  1165. X    EXT char nocd[] INIT("Can't chdir to directory %s\n");
  1166. X#else
  1167. X    EXT char nocd[] INIT("Can't find %s\n");
  1168. X#endif
  1169. X
  1170. X#ifdef NOLINEBUF
  1171. X#define FLUSH ,fflush(stdout)
  1172. X#else
  1173. X#define FLUSH
  1174. X#endif
  1175. X
  1176. X#ifdef lint
  1177. X#undef FLUSH
  1178. X#define FLUSH
  1179. X#undef putchar
  1180. X#define putchar(c)
  1181. X#endif
  1182. END_OF_FILE
  1183.   if test 28180 -ne `wc -c <'common.h'`; then
  1184.     echo shar: \"'common.h'\" unpacked with wrong size!
  1185.   fi
  1186.   # end of 'common.h'
  1187. fi
  1188. if test -f 'mthreads.c' -a "${1}" != "-c" ; then 
  1189.   echo shar: Will not clobber existing file \"'mthreads.c'\"
  1190. else
  1191.   echo shar: Extracting \"'mthreads.c'\" \(29761 characters\)
  1192.   sed "s/^X//" >'mthreads.c' <<'END_OF_FILE'
  1193. X/* $Header: mthreads.c,v 4.3.3.3 91/01/18 19:05:00 davison Trn $
  1194. X**
  1195. X** $Log:    mthreads.c,v $
  1196. X** Revision 4.3.3.3  91/01/18  19:05:00  davison
  1197. X** Modified the way signals are handled to avoid endless loops.  Added -s & -z
  1198. X** options.  Fixed a truncate bug and a problem with new groups not processing.
  1199. X** 
  1200. X** Revision 4.3.3.2  90/08/20  16:43:19  davison
  1201. X** Implemented new command-line interface and database upgrading.
  1202. X** 
  1203. X** Revision 4.3.3.1  90/07/24  22:24:17  davison
  1204. X** Initial Trn Release
  1205. X** 
  1206. X*/
  1207. X
  1208. X/* mthreads.c -- for making and updating a discussion-thread database
  1209. X**
  1210. X** We use the active file as our high/low counts for each group, and create
  1211. X** an active2 file of our own to keep track of the high/lows of the database.
  1212. X** When fully updated, the two files should be identical.  This gives us
  1213. X** quick access to the last processed high/low counts without opening
  1214. X** each data file, PLUS it allows trn to use the fake active file as if it
  1215. X** were the real thing to keep it from seeing articles before they are
  1216. X** processed.  If the active2 file is removed or corrupted, it will be
  1217. X** automatically repaired in the normal course of operation.  We update
  1218. X** the file IN PLACE so that trn can keep it open all the time.  Normally
  1219. X** the size of the file does not change, so it is easy to do.  In those
  1220. X** rare instances where a news admin shuffles the real active file, we
  1221. X** take it all in stride by throwing a little memory at the problem.
  1222. X**
  1223. X** Usage:  mthreads [-d[MM]] [-e[HHMM]] [-aDfknv] [hierarchy_list]
  1224. X*/
  1225. X
  1226. X#include "EXTERN.h"
  1227. X#include "common.h"
  1228. X#ifdef SERVER
  1229. X#include "server.h"
  1230. X#endif
  1231. X#include "INTERN.h"
  1232. X#include "mthreads.h"
  1233. X
  1234. X#ifdef TZSET
  1235. X#include <time.h>
  1236. X#else
  1237. X#include <sys/time.h>
  1238. X#include <sys/timeb.h>
  1239. X#endif
  1240. X
  1241. XFILE *fp_lock, *fp_log;
  1242. X
  1243. Xstruct stat filestat;
  1244. X
  1245. Xstatic char line[256];
  1246. Xstatic char line2[256];
  1247. X
  1248. X/* If you want to change the field size, do it once and then leave it alone. */
  1249. Xchar fmt_active2[] = "%s %06ld %06ld %c\n";
  1250. X
  1251. Xchar *filename;
  1252. X
  1253. Xtypedef struct _active_line {
  1254. X    struct _active_line *link;
  1255. X    char *name;
  1256. X    long last;
  1257. X    long first;
  1258. X    char type;
  1259. X} ACTIVE_LINE;
  1260. X
  1261. X#define Nullact Null(ACTIVE_LINE*)
  1262. X
  1263. XACTIVE_LINE *line_root = Nullact, *last_line = Nullact, *pline = Nullact;
  1264. X
  1265. Xbool force_flag = FALSE, kill_mthreads = FALSE, no_processing = FALSE;
  1266. Xbool add_new = FALSE, rebuild = FALSE, zap_thread = FALSE, grevious_error;
  1267. Xint daemon_delay = 0, log_verbosity = 0, debug = 0, slow_down = 0;
  1268. Xlong expire_time = 0;
  1269. Xchar *hierarchy_list = NULL;
  1270. Xlong truncate_len = -1;
  1271. X
  1272. Xchar nullstr[] = "";
  1273. X
  1274. XBMAP my_bmap, mt_bmap;
  1275. X
  1276. X#ifdef TZSET
  1277. Xtime_t tnow;
  1278. X#else
  1279. Xstruct timeb ftnow;
  1280. X#endif
  1281. X
  1282. X#define TIMER_FIRST 1
  1283. X#define TIMER_DEFAULT (10 * 60)
  1284. X
  1285. Xint added_groups, removed_groups, action;
  1286. X
  1287. X#define NG_DEFAULT    0
  1288. X#define NG_MATCH    1
  1289. X#define NG_SKIP        2
  1290. X
  1291. X#ifdef SERVER
  1292. Xchar *server;
  1293. X#else
  1294. Xtime_t last_modified;
  1295. X#endif
  1296. X
  1297. XSIGRET alarm_handler(), int_handler(), severe_handler();
  1298. Xvoid makethreads(), wrap_it_up();
  1299. X
  1300. Xmain( argc, argv )
  1301. Xint  argc;
  1302. Xchar *argv[];
  1303. X{
  1304. X    int fd;
  1305. X    long pid;
  1306. X
  1307. X    while( --argc ) {
  1308. X    if( **++argv == '-' ) {
  1309. X        while( *++*argv ) {
  1310. X        switch( **argv ) {
  1311. X        case 'a':        /* automatically thread new groups */
  1312. X            add_new = TRUE;
  1313. X            break;
  1314. X        case 'D':
  1315. X            debug++;
  1316. X            break;
  1317. X        case 'd':
  1318. X            if( *++*argv <= '9' && **argv >= '0' ) {
  1319. X            daemon_delay = atoi( *argv ) * 60;
  1320. X            while( *++*argv <= '9' && **argv >= '0' ) {
  1321. X                ;
  1322. X            }
  1323. X            } else {
  1324. X            daemon_delay = TIMER_DEFAULT;
  1325. X            }
  1326. X            --*argv;
  1327. X            break;
  1328. X        case 'e': {
  1329. X            struct tm *ts;
  1330. X            long desired;
  1331. X
  1332. X            (void) time( &expire_time );
  1333. X            ts = localtime( &expire_time );
  1334. X
  1335. X            if( *++*argv <= '9' && **argv >= '0' ) {
  1336. X            desired = atol( *argv );
  1337. X            if( desired/100 > 23 || desired%100 > 59 ) {
  1338. X                fprintf( stderr, "Illegal expire time: '%04d'\n",
  1339. X                desired );
  1340. X                exit( 1 );
  1341. X            }
  1342. X            desired = (desired/100)*60 + desired%100;
  1343. X            while( *++*argv <= '9' && **argv >= '0' ) {
  1344. X                ;
  1345. X            }
  1346. X            } else {
  1347. X            desired = 30;            /* 0030 = 12:30am */
  1348. X            }
  1349. X            --*argv;
  1350. X            desired -= ts->tm_hour * 60 + ts->tm_min;
  1351. X            if( desired < 0 ) {
  1352. X            desired += 24 * 60;
  1353. X            }
  1354. X            expire_time += desired * 60 - ts->tm_sec;
  1355. X            break;
  1356. X         }
  1357. X        case 'f':
  1358. X            force_flag = TRUE;
  1359. X            break;
  1360. X        case 'k':
  1361. X            kill_mthreads = TRUE;
  1362. X            break;
  1363. X        case 'n':
  1364. X            no_processing = TRUE;
  1365. X            break;
  1366. X        case 's':
  1367. X            slow_down++;
  1368. X            break;
  1369. X        case 'v':
  1370. X            log_verbosity++;
  1371. X            break;
  1372. X        case 'z':
  1373. X            zap_thread = TRUE;
  1374. X            break;
  1375. X        default:
  1376. X            fprintf( stderr, "Unknown option: '%c'\n", **argv );
  1377. X            exit( 1 );
  1378. X        }
  1379. X        }
  1380. X    } else {
  1381. X        if( hierarchy_list ) {
  1382. X        fprintf( stderr, "Specify the newsgroups in one comma-separated list.\n" );
  1383. X        exit( 1 );
  1384. X        }
  1385. X        hierarchy_list = *argv;
  1386. X    }
  1387. X    }
  1388. X
  1389. X    /* Set up a nice friendly umask. */
  1390. X    umask( 002 );
  1391. X
  1392. X    /* What time is it? */
  1393. X#ifdef TZSET
  1394. X    (void) time( &tnow );
  1395. X    (void) tzset();
  1396. X#else
  1397. X    (void) ftime( &ftnow );
  1398. X#endif
  1399. X
  1400. X    /* Make sure we're not already running by creating a lock file.
  1401. X    ** (I snagged this method from C news.)
  1402. X    */
  1403. X    sprintf( line, "%s.%d", file_exp( "%X/LOCK" ), getpid() );
  1404. X    if( (fp_lock = fopen( line, "w" )) == Nullfp ) {
  1405. X    fprintf( stderr, "Unable to create lock temporary `%s'.\n", line );
  1406. X    exit( 1 );
  1407. X    }
  1408. X    fprintf( fp_lock, "%d\n", getpid() );
  1409. X    fclose( fp_lock );
  1410. X
  1411. X    /* Try to link to lock file. */
  1412. X    filename = file_exp( "%X/LOCKmthreads" );
  1413. X  dolink:
  1414. X    if( link( line, filename ) < 0 ) {
  1415. X      long otherpid;
  1416. X    /* Try to avoid possible race with daemon starting up. */
  1417. X    sleep (5);
  1418. X    if( (fp_lock = fopen( filename, "r")) == Nullfp ) {
  1419. X        fprintf( stderr, "unable to open %s\n", filename );
  1420. X        unlink( line );
  1421. X        exit( 1 );
  1422. X    }
  1423. X    if( fscanf( fp_lock, "%ld", &otherpid ) != 1) { 
  1424. X        fprintf( stderr, "unable to read pid from %s\n", filename );
  1425. X        unlink( line );
  1426. X        fclose( fp_lock );
  1427. X        exit( 1 );
  1428. X    }
  1429. X    fclose( fp_lock );
  1430. X    if( kill( otherpid, kill_mthreads ? SIGTERM : 0 ) == -1
  1431. X     && errno == ESRCH ) {
  1432. X        if( unlink( filename ) == -1 ) {
  1433. X        fprintf( stderr, "unable to unlink lockfile %s\n", filename );
  1434. X        unlink( line );
  1435. X        exit( 1 );
  1436. X        }
  1437. X        if( !kill_mthreads ) {
  1438. X        goto dolink;
  1439. X        }
  1440. X    }
  1441. X    unlink( line );
  1442. X    if( kill_mthreads ) {
  1443. X        fprintf( stderr, "killing currently running mthreads.\n" );
  1444. X        exit( 0 );
  1445. X    } else {
  1446. X        fprintf( stderr, "mthreads is already running.\n" );
  1447. X        exit( 1 );
  1448. X    }
  1449. X    }
  1450. X
  1451. X    unlink( line );            /* remove temporary LOCK.<pid> file */
  1452. X
  1453. X    if( kill_mthreads ) {
  1454. X    fprintf( stderr, "mthreads is not running.\n" );
  1455. X    exit( 1 );
  1456. X    }
  1457. X
  1458. X    /* Open our log file */
  1459. X    filename = file_exp( "%X/mt.log" );
  1460. X    if( (fp_log = fopen( filename, "a" )) == Nullfp ) {
  1461. X    fprintf( stderr, "Unable to open `%s'.\n", filename );
  1462. X    exit( 1 );
  1463. X    }
  1464. X
  1465. X#ifdef SIGHUP
  1466. X    if( sigset( SIGHUP, SIG_IGN ) != SIG_IGN ) {
  1467. X    sigset( SIGHUP, int_handler );
  1468. X    }
  1469. X#endif
  1470. X    if( sigset( SIGINT, SIG_IGN ) != SIG_IGN ) {
  1471. X    sigset( SIGINT, int_handler );
  1472. X    }
  1473. X#ifdef SIGQUIT
  1474. X    if( sigset( SIGQUIT, SIG_IGN ) != SIG_IGN ) {
  1475. X    sigset( SIGQUIT, int_handler );
  1476. X    }
  1477. X#endif
  1478. X    sigset( SIGTERM, int_handler );
  1479. X#ifdef SIGBUS
  1480. X    sigset( SIGBUS, severe_handler );
  1481. X#endif
  1482. X    sigset( SIGSEGV, severe_handler );
  1483. X#ifdef SIGTTIN
  1484. X    sigset( SIGTTIN, SIG_IGN );
  1485. X    sigset( SIGTTOU, SIG_IGN );
  1486. X#endif
  1487. X    sigset( SIGALRM, SIG_IGN );
  1488. X#ifdef lint
  1489. X    alarm_handler();            /* foolishness for lint's sake */
  1490. X    int_handler( SIGINT );
  1491. X    severe_handler( SIGSEGV );
  1492. X#endif
  1493. X
  1494. X    /* Ensure this machine has the right byte-order for the database */
  1495. X    filename = file_exp( "%X/db.init" );
  1496. X    if( (fp_lock = fopen( filename, FOPEN_RB )) == Nullfp
  1497. X     || fread( &mt_bmap, 1, sizeof (BMAP), fp_lock ) < sizeof (BMAP)-1 ) {
  1498. X    if( fp_lock != Nullfp ) {
  1499. X        fclose( fp_lock );
  1500. X    }
  1501. X     write_db_init:
  1502. X    mybytemap( &mt_bmap );
  1503. X    if( (fp_lock = fopen( filename, FOPEN_WB )) == Nullfp ) {
  1504. X        log_entry( "Unable to create file: `%s'.\n", filename );
  1505. X        exit( 1 );
  1506. X    }
  1507. X    mt_bmap.version = DB_VERSION;
  1508. X    fwrite( &mt_bmap, 1, sizeof (BMAP), fp_lock );
  1509. X    fclose( fp_lock );
  1510. X    } else {
  1511. X    int i;
  1512. X
  1513. X    fclose( fp_lock );
  1514. X    if( mt_bmap.version != DB_VERSION ) {
  1515. X        if( mt_bmap.version == DB_VERSION-1 ) {
  1516. X        rebuild = TRUE;
  1517. X        log_entry( "Upgrading database to version %d.\n", DB_VERSION );
  1518. X        goto write_db_init;
  1519. X        }
  1520. X        log_entry( "** Database is not the right version (%d instead of %d) **\n",
  1521. X        mt_bmap.version, DB_VERSION );
  1522. X        exit( 1 );
  1523. X    }
  1524. X    mybytemap( &my_bmap );
  1525. X    for( i = 0; i < sizeof (LONG); i++ ) {
  1526. X        if( my_bmap.l[i] != mt_bmap.l[i]
  1527. X         || (i < sizeof (WORD) && my_bmap.w[i] != mt_bmap.w[i]) ) {
  1528. X        log_entry( "\
  1529. X** Byte-order conflict -- re-run from a compatible machine **\n\
  1530. X\t\tor remove the current thread files, including db.init **\n" );
  1531. X        exit( 1 );
  1532. X        }
  1533. X    }
  1534. X    }
  1535. X
  1536. X#ifdef SERVER
  1537. X    server = getserverbyfile( SERVER_FILE );
  1538. X    if( server == NULL ) {
  1539. X    log_entry( "Couldn't find name of news server.\n" );
  1540. X    exit( 1 );
  1541. X    }
  1542. X#endif
  1543. X
  1544. X    /* If we're not in daemon mode, run through once and quit. */
  1545. X    if( !daemon_delay ) {
  1546. X    log_entry( "mthreads single pass started.\n" );
  1547. X    setbuf( stdout, Nullch );
  1548. X    extra_expire = (expire_time != 0);
  1549. X    makethreads();
  1550. X    } else {
  1551. X    /* For daemon mode, we cut ourself off from anything tty-related and
  1552. X    ** run in the background (involves forks, but no knives).
  1553. X    */
  1554. X    close( 0 );
  1555. X    if( open( "/dev/null", 2 ) != 0 ) {
  1556. X        fprintf( stderr, "unable to open /dev/null!\n" );
  1557. X        exit( 1 );
  1558. X    }
  1559. X    close( 1 );
  1560. X    close( 2 );
  1561. X    dup( 0 );
  1562. X    dup( 0 );
  1563. X    while( (pid = fork()) < 0 ) {
  1564. X        sleep( 2 );
  1565. X    }
  1566. X    if( pid ) {
  1567. X        exit( 0 );
  1568. X    }
  1569. X#ifdef TIOCNOTTY
  1570. X    if( (fd = open( "/dev/tty", 1 )) >= 0 ) {
  1571. X        ioctl( fd, TIOCNOTTY, (int*)0 );
  1572. X        close( fd );
  1573. X    }
  1574. X#else
  1575. X    (void) setpgrp();
  1576. X    while( (pid = fork()) < 0 ) {
  1577. X        sleep( 2 );
  1578. X    }
  1579. X    if( pid ) {
  1580. X        exit( 0 );
  1581. X    }
  1582. X#endif
  1583. X    /* Put our pid in the lock file for death detection */
  1584. X    if( (fp_lock = fopen( file_exp( "%X/LOCKmthreads" ), "w" )) != Nullfp ) {
  1585. X        fprintf( fp_lock, "%d\n", getpid() );
  1586. X        fclose( fp_lock );
  1587. X    }
  1588. X
  1589. X    log_entry( "mthreads daemon started.\n" );
  1590. X
  1591. X#ifndef SERVER
  1592. X    last_modified = 0;
  1593. X#endif
  1594. X    sigset( SIGALRM, alarm_handler );
  1595. X
  1596. X    /* Start timer -- first interval is shorter than all others */
  1597. X    alarm( TIMER_FIRST );
  1598. X    for( ;; ) {
  1599. X        if( caught_interrupt ) {
  1600. X        wrap_it_up( 0 );
  1601. X        /* NORETURN */
  1602. X        }
  1603. X        pause();        /* let alarm go off */
  1604. X        if( caught_interrupt ) {
  1605. X        wrap_it_up( 0 );
  1606. X        /* NORETURN */
  1607. X        }
  1608. X        alarm( 0 );
  1609. X
  1610. X        /* Re-open our log file, if needed */
  1611. X        if( !fp_log && !(fp_log = fopen( file_exp( "%X/mt.log" ), "a" )) ) {
  1612. X        wrap_it_up( 1 );
  1613. X        }
  1614. X#ifndef SERVER
  1615. X        if( stat( file_exp( ACTIVE ), &filestat ) < 0 ) {
  1616. X        log_entry( "Unable to stat active file -- quitting.\n" );
  1617. X        wrap_it_up( 1 );
  1618. X        }
  1619. X#endif
  1620. X        if( expire_time && time( 0L ) > expire_time ) {
  1621. X        expire_time += 24L * 60 * 60;
  1622. X        extra_expire = TRUE;
  1623. X        }
  1624. X#ifdef SERVER
  1625. X        if( 1 ) {        /* always compare files */
  1626. X#else
  1627. X        if( extra_expire || filestat.st_mtime != last_modified ) {
  1628. X        last_modified = filestat.st_mtime;
  1629. X#endif
  1630. X        makethreads();
  1631. X        }
  1632. X        alarm( daemon_delay );
  1633. X        fclose( fp_log );    /* close the log file while we sleep */
  1634. X        fp_log = Nullfp;
  1635. X    } /* for */
  1636. X    }/* if */
  1637. X
  1638. X    wrap_it_up( 0 );
  1639. X}
  1640. X
  1641. XSIGRET
  1642. Xalarm_handler()
  1643. X{
  1644. X    sigset( SIGALRM, alarm_handler );
  1645. X}
  1646. X
  1647. XSIGRET
  1648. Xint_handler( sig )
  1649. Xint sig;
  1650. X{
  1651. X    /* Flag interrupt occurred -- main loop attempts an orderly retreat. */
  1652. X    if( ++caught_interrupt >= 3 ) {
  1653. X    wrap_it_up( 1 );
  1654. X    }
  1655. X    /* Re-open our log file, if needed */
  1656. X    if( fp_log || (fp_log = fopen( file_exp( "%X/mt.log" ), "a" )) ) {
  1657. X    if( sig == SIGTERM ) {
  1658. X        log_entry( "mthreads halted.\n", sig);
  1659. X    } else {
  1660. X        log_entry( "Interrupt %d received.\n", sig);
  1661. X    }
  1662. X    }
  1663. X    if( !daemon_delay ) {
  1664. X    printf( "interrupt %d!\n", sig );
  1665. X    }
  1666. X}
  1667. X
  1668. X/* Severe interrupts require severe action -- abort immediately, possibly
  1669. X** removing the thread file on the way.
  1670. X*/
  1671. XSIGRET
  1672. Xsevere_handler( sig )
  1673. Xint sig;
  1674. X{
  1675. X    /* Let's be a bit paranoid here -- avoid any possibility of looping. */
  1676. X    if( caught_interrupt >= 10 ) {
  1677. X    wrap_it_up( 1 );
  1678. X    }
  1679. X    caught_interrupt = 10;
  1680. X
  1681. X    /* Destroy offending thread file if requested to do so. */
  1682. X    if( zap_thread ) {
  1683. X    unlink( thread_name( line ) );
  1684. X    }
  1685. X    /* Re-open our log file, if needed */
  1686. X    if( fp_log || (fp_log = fopen( file_exp( "%X/mt.log" ), "a" )) ) {
  1687. X    log_error( "** Severe signal: %d **\n", sig );
  1688. X    if( zap_thread ) {
  1689. X        log_entry( "Destroyed thread file for %s\n", line );
  1690. X    }
  1691. X    }
  1692. X    if( !daemon_delay ) {
  1693. X    printf( "Severe signal: %d!\n", sig);
  1694. X    if( zap_thread ) {
  1695. X        printf( "Destroyed thread file for %s\n", line );
  1696. X    }
  1697. X    }
  1698. X    wrap_it_up( 1 );
  1699. X}
  1700. X
  1701. Xvoid
  1702. Xwrap_it_up( ret )
  1703. Xint ret;
  1704. X{
  1705. X    unlink( file_exp( "%X/LOCKmthreads" ) );        /* remove lock */
  1706. X
  1707. X    exit( ret );
  1708. X}
  1709. X
  1710. X/* Process the active file, creating/modifying the active2 file and
  1711. X** creating/modifying the thread data files.
  1712. X*/
  1713. Xvoid
  1714. Xmakethreads()
  1715. X{
  1716. X    register char *cp, *cp2;
  1717. X    FILE *fp_active, *fp_active2, *fp_active3;
  1718. X    long first, last, first2, last2;
  1719. X    char ch, ch2;
  1720. X    char data_file_open;
  1721. X    bool update_successful;
  1722. X    bool eof_active = FALSE, eof_active2 = FALSE;
  1723. X
  1724. X#ifdef SERVER
  1725. X    switch( server_init( server ) ) {
  1726. X    case OK_NOPOST:
  1727. X    case OK_CANPOST:
  1728. X    break;
  1729. X    case ERR_ACCESS:
  1730. X    log_entry( "Server %s rejected connection -- quitting.\n", server );
  1731. X    wrap_it_up( 1 );
  1732. X    default:
  1733. X    log_entry( "Couldn't connect with server %s -- sleeping.\n", server );
  1734. X    return;
  1735. X    }
  1736. X    put_server( "LIST" );    /* ask server for the active file */
  1737. X    get_server( line, sizeof line );
  1738. X    if( *line != CHAR_OK ) {
  1739. X    log_entry( "Unable to get active file from server -- sleeping.\n" );
  1740. X    close_server();
  1741. X    return;
  1742. X    }
  1743. X    if( (fp_active = fopen( file_exp( ACTIVE1 ), "w+" )) == Nullfp ) {
  1744. X    log_entry( "Unable to write the active1 file.\n" );
  1745. X    wrap_it_up( 1 );
  1746. X    }
  1747. X    while( 1 ) {
  1748. X    if( caught_interrupt ) {
  1749. X        wrap_it_up( 0 );
  1750. X        /* NORETURN */
  1751. X    }
  1752. X    if( get_server( line, sizeof line ) < 0 ) {
  1753. X        log_entry( "Server failed to send entire active file -- sleeping.\n" );
  1754. X        fclose( fp_active );
  1755. X        close_server();
  1756. X        return;
  1757. X    }
  1758. X    if( *line == '.' ) {
  1759. X        break;
  1760. X    }
  1761. X    fputs( line, fp_active );
  1762. X    putc( '\n', fp_active );
  1763. X    }
  1764. X    fseek( fp_active, 0L, 0 );        /* rewind for read */
  1765. X#else
  1766. X    if( (fp_active = fopen( file_exp( ACTIVE ), "r" )) == Nullfp ) {
  1767. X    log_entry( "Unable to open the active file.\n" );
  1768. X    wrap_it_up( 1 );
  1769. X    }
  1770. X#endif
  1771. X    filename = file_exp( ACTIVE2 );
  1772. X    if( (fp_active3 = fopen( filename, "r+" )) == Nullfp ) {
  1773. X    if( (fp_active3 = fopen( filename, "w" )) == Nullfp ) {
  1774. X        log_entry( "Unable to open the active2 file for update.\n" );
  1775. X        wrap_it_up( 1 );
  1776. X    }
  1777. X    }
  1778. X    if( (fp_active2 = fopen( filename, "r" )) == Nullfp ) {
  1779. X    log_entry( "Unable to open the active2 file.\n" );
  1780. X    wrap_it_up( 1 );
  1781. X    }
  1782. X    if( caught_interrupt ) {
  1783. X    wrap_it_up( 0 );
  1784. X    /* NORETURN */
  1785. X    }
  1786. X    if( extra_expire && log_verbosity ) {
  1787. X    log_entry( "Using enhanced expiration for this pass.\n" );
  1788. X    }
  1789. X
  1790. X    processed_groups = added_groups = removed_groups = 0;
  1791. X    added_articles = expired_articles = 0;
  1792. X
  1793. X    /* Loop through entire active file. */
  1794. X    for( ;; ) {
  1795. X    if( eof_active || !fgets( line, sizeof line, fp_active ) ) {
  1796. X        if( eof_active2 && !line_root ) {
  1797. X        break;
  1798. X        }
  1799. X        eof_active = TRUE;
  1800. X        ch = 'x';
  1801. X    } else {
  1802. X        if( !(cp = index( line, ' ' )) ) {
  1803. X        log_entry( "active line has no space: %s\n", line );
  1804. X        continue;
  1805. X        }
  1806. X        *cp = '\0';
  1807. X        if( sscanf( cp+1, "%ld %ld %c", &last, &first, &ch ) != 3 ) {
  1808. X        log_entry( "active digits corrupted: %s %s\n", line, cp+1 );
  1809. X        continue;
  1810. X        }
  1811. X    }
  1812. X    if( debug || log_verbosity > 3 ) {
  1813. X        log_entry( "Processing %s:\n", line );
  1814. X    }
  1815. X    data_file_open = 0;
  1816. X    /* If we've allocated some lines in memory while searching for
  1817. X    ** newsgroups (they've scrambled the active file on us), check
  1818. X    ** them first.
  1819. X    */
  1820. X    last_line = Nullact;
  1821. X    for( pline = line_root; pline; pline = pline->link ) {
  1822. X        if( eof_active || strEQ( line, pline->name ) ) {
  1823. X        strcpy( line2, pline->name );
  1824. X        free( pline->name );
  1825. X        first2 = pline->first;
  1826. X        last2 = pline->last;
  1827. X        ch2 = pline->type;
  1828. X        if( last_line ) {
  1829. X            last_line->link = pline->link;
  1830. X        } else {
  1831. X            line_root = pline->link;
  1832. X        }
  1833. X        free( pline );
  1834. X        break;
  1835. X        }
  1836. X        last_line = pline;
  1837. X    }/* for */
  1838. X    /* If not found yet, check the active2 file. */
  1839. X    if( !pline ) {
  1840. X        for( ;; ) {
  1841. X        if( eof_active2 || !fgets( line2, sizeof line2, fp_active2 ) ) {
  1842. X            /* At end of file, check if the thread data file exists.
  1843. X            ** If so, use its high/low values.  Else, default to
  1844. X            ** some initial values.
  1845. X            */
  1846. X            eof_active2 = TRUE;
  1847. X            if( eof_active ) {
  1848. X            break;
  1849. X            }
  1850. X            strcpy( line2, line );
  1851. X            if( (data_file_open = init_data( thread_name( line ) )) ) {
  1852. X            last2 = total.last;
  1853. X            first2 = total.first;
  1854. X            ch2 = 'y';
  1855. X            } else {
  1856. X            total.first = first2 = first;
  1857. X            if( add_new ) {
  1858. X                total.last = last2 = first - 1;
  1859. X                ch2 = (ch == '=' ? 'x' : ch);
  1860. X                added_groups++;
  1861. X            } else {
  1862. X                total.last = last2 = last;
  1863. X                ch2 = (ch == '=' ? 'X' : toupper( ch ));
  1864. X            }
  1865. X            }
  1866. X            data_file_open++;        /* (1 == empty, 2 == open) */
  1867. X            break;
  1868. X        }
  1869. X        if( !(cp2 = index( line2, ' ' )) ) {
  1870. X            log_entry( "active2 line has no space: %s\n", line2 );
  1871. X            continue;
  1872. X        }
  1873. X        *cp2 = '\0';
  1874. X        if( sscanf( cp2+1,"%ld %ld %c",&last2,&first2,&ch2 ) != 3 ) {
  1875. X            log_entry( "active2 digits corrupted: %s %s\n",
  1876. X            line2, cp2+1 );
  1877. X            continue;
  1878. X        }
  1879. X        /* Check if we're still in-sync */
  1880. X        if( eof_active || strEQ( line, line2 ) ) {
  1881. X            break;
  1882. X        }
  1883. X        /* Nope, we've got to go looking for this line somewhere
  1884. X        ** down in the file.  Save each non-matching line in memory
  1885. X        ** as we go.
  1886. X        */
  1887. X        pline = (ACTIVE_LINE*)safemalloc( sizeof (ACTIVE_LINE) );
  1888. X        pline->name = savestr( line2 );
  1889. X        pline->last = last2;
  1890. X        pline->first = first2;
  1891. X        pline->type = ch2;
  1892. X        pline->link = Nullact;
  1893. X        if( !last_line ) {
  1894. X            line_root = pline;
  1895. X        } else {
  1896. X            last_line->link = pline;
  1897. X        }
  1898. X        last_line = pline;
  1899. X        }/* for */
  1900. X        if( eof_active && eof_active2 ) {
  1901. X        break;
  1902. X        }
  1903. X    }/* if !pline */
  1904. X    if( eof_active ) {
  1905. X        strcpy( line, line2 );
  1906. X        if( truncate_len < 0 ) {
  1907. X        truncate_len = ftell( fp_active3 );
  1908. X        }
  1909. X    }
  1910. X    if( rebuild ) {
  1911. X        unlink( thread_name( line ) );
  1912. X    }
  1913. X    update_successful = FALSE;
  1914. X    if( hierarchy_list ) {
  1915. X        action = ngmatch( hierarchy_list, line );
  1916. X    } else {
  1917. X        action = NG_DEFAULT;
  1918. X    }
  1919. X    switch( action ) {
  1920. X    case NG_DEFAULT:
  1921. X        if( ch2 < 'a' ) {
  1922. X        action = NG_SKIP;
  1923. X        } else {
  1924. X        action = NG_MATCH;
  1925. X        }
  1926. X        break;
  1927. X    case NG_MATCH:                /* add if unthreaded */
  1928. X        if( ch2 < 'a' ) {
  1929. X        total.last = last2 = first2 - 1;
  1930. X        added_groups++;
  1931. X        }
  1932. X        break;
  1933. X    case NG_SKIP:                /* remove if threaded */
  1934. X        if( ch2 >= 'a' && !debug ) {
  1935. X        unlink( thread_name( line ) );
  1936. X        removed_groups++;
  1937. X        }
  1938. X        break;
  1939. X    }
  1940. X    if( caught_interrupt || (debug && action != NG_MATCH) ) {
  1941. X        dont_read_data( data_file_open );    /* skip silently */
  1942. X    } else if( ch == 'x' || ch == '=' ) {
  1943. X        if( !daemon_delay ) {        /* skip 'x'ed groups */
  1944. X        putchar( 'x' );
  1945. X        }
  1946. X        ch = (action == NG_SKIP ? 'X' : 'x');
  1947. X        if( (ch2 >= 'a' && ch2 != 'x') || force_flag ) {
  1948. X        /* Remove thread file if group is newly 'x'ed out */
  1949. X        unlink( thread_name( line ) );
  1950. X        }
  1951. X        update_successful = TRUE;
  1952. X        dont_read_data( data_file_open );
  1953. X    } else if( action == NG_SKIP ) {    /* skip excluded groups */
  1954. X        if( !daemon_delay ) {
  1955. X        putchar( 'X' );
  1956. X        }
  1957. X        ch = toupper( ch );
  1958. X        if( force_flag ) {
  1959. X        unlink( thread_name( line ) );
  1960. X        }
  1961. X        update_successful = TRUE;
  1962. X        dont_read_data( data_file_open );
  1963. X    } else if( no_processing ) {
  1964. X        if( !daemon_delay ) {
  1965. X        putchar( ',' );
  1966. X        }
  1967. X        ch2 = ch;
  1968. X        dont_read_data( data_file_open );
  1969. X    } else if( !force_flag && !extra_expire && !rebuild
  1970. X     && first == first2 && last == last2 ) {
  1971. X        /* We're up-to-date here.  Skip it. */
  1972. X        if( !daemon_delay ) {
  1973. X        putchar( '.' );
  1974. X        }
  1975. X        update_successful = TRUE;
  1976. X        dont_read_data( data_file_open );
  1977. X    } else {
  1978. X        /* Looks like we need to process something. */
  1979. X#ifdef SERVER
  1980. X        sprintf( line2, "GROUP %s", line );
  1981. X        put_server( line2 );        /* go to next group */
  1982. X        if( get_server( line2, sizeof line2 ) < 0 || *line2 != CHAR_OK ) {
  1983. X        log_entry( "NNTP failure on group `%s'.\n", line );
  1984. X#else
  1985. X        cp = line2;
  1986. X        while( (cp = index( cp, '.' )) ) {
  1987. X        *cp = '/';
  1988. X        }
  1989. X        filename = file_exp( line2 );    /* relative to spool dir */
  1990. X        if( chdir( filename ) < 0 ) {
  1991. X        if (errno != ENOENT) {
  1992. X            log_entry( "Unable to chdir to `%s'.\n", filename );
  1993. X        }
  1994. X#endif
  1995. X        if( !daemon_delay ) {
  1996. X            putchar( '*' );
  1997. X        }
  1998. X        dont_read_data( data_file_open );
  1999. X        } else {
  2000. X        filename = thread_name( line );
  2001. X        /* Try to open the data file only if we didn't try it
  2002. X        ** in the name matching code above.
  2003. X        */
  2004. X        if( !data_file_open-- ) {    /* (0 == haven't tried yet) */
  2005. X            if( !(data_file_open = init_data( filename )) ) {
  2006. X            total.last = first - 1;
  2007. X            total.first = first;
  2008. X            }
  2009. X        }
  2010. X        strcpy( line2, filename );
  2011. X        cp = rindex( line2, '/' ) + 1;
  2012. X
  2013. X        if( data_file_open ) {        /* (0 == empty, 1 == open) */
  2014. X            if( !read_data() ) {    /* did read fail? */
  2015. X#ifndef DEBUG
  2016. X            unlink( filename );    /* trash input file */
  2017. X#else
  2018. X            strcpy( cp, "bad.read" );
  2019. X            rename( filename, line2 );
  2020. X#endif
  2021. X            data_file_open = init_data( filename );
  2022. X            total.last = first - 1;
  2023. X            total.first = first;
  2024. X            }
  2025. X        }
  2026. X        grevious_error = FALSE;
  2027. X        process_articles( first, last );
  2028. X        processed_groups++;
  2029. X        if( caught_interrupt ) {
  2030. X            processed_groups--;    /* save nothing -- no update */
  2031. X        } else if( !added_count && !expired_count && last == last2 ) {
  2032. X            (void) write_data( Nullch );
  2033. X            if( !daemon_delay ) {
  2034. X            putchar( ':' );
  2035. X            }
  2036. X            update_successful = TRUE;
  2037. X        } else if( !total.root ) {
  2038. X            /* When the data file goes empty, remove it. */
  2039. X            unlink( filename );
  2040. X            expired_articles += expired_count;
  2041. X            if( !daemon_delay ) {
  2042. X            putchar( '-' );
  2043. X            }
  2044. X            update_successful = TRUE;
  2045. X        } else {
  2046. X            strcpy( cp, NEW_THREAD );    /* write data as .new */
  2047. X            if( write_data( line2 ) && !grevious_error ) {
  2048. X            rename( line2, filename );
  2049. X            added_articles += added_count;
  2050. X            expired_articles += expired_count;
  2051. X            if( !daemon_delay ) {
  2052. X                putchar( '#' );
  2053. X            }
  2054. X            update_successful = TRUE;
  2055. X            } else {
  2056. X#ifndef DEBUG
  2057. X            unlink( line2 );    /* blow-away bad write */
  2058. X#else
  2059. X            cp = rindex( filename, '/' ) + 1;
  2060. X            strcpy( cp, "bad.write" );
  2061. X            rename( line2, filename );
  2062. X#endif
  2063. X            if( !daemon_delay ) {
  2064. X                putchar( '!' );
  2065. X            }
  2066. X            }/* if */
  2067. X        }/* if */
  2068. X        }/* if */
  2069. X    }/* if */
  2070. X    /* Finally, update the active2 entry for this newsgroup. */
  2071. X    if( update_successful ) {
  2072. X        fprintf( fp_active3, fmt_active2, line, last, first, ch );
  2073. X    } else {
  2074. X        fprintf( fp_active3, fmt_active2, line, last2, first2, ch2 );
  2075. X    }
  2076. X    /* If we're not out of sync, keep active2 file flushed. */
  2077. X    if( !line_root ) {
  2078. X        fflush( fp_active3 );
  2079. X    }
  2080. X    }/* for */
  2081. X
  2082. X#ifdef SERVER
  2083. X    close_server();
  2084. X#endif
  2085. X    fclose( fp_active );
  2086. X    fclose( fp_active2 );
  2087. X    fclose( fp_active3 );
  2088. X
  2089. X    if( truncate_len >= 0 ) {
  2090. X#ifdef TRUNCATE
  2091. X    if( truncate( file_exp( ACTIVE2 ), truncate_len ) == -1 )
  2092. X        log_entry( "Unable to truncate the active2 file.\n" );
  2093. X#else
  2094. X#ifdef CHSIZE
  2095. X    int fd;
  2096. X    if( (fd = open( file_exp( ACTIVE2 ), O_RDWR )) == -1 )
  2097. X        log_entry( "Unable to open the active2 file for truncation.\n" );
  2098. X    else {
  2099. X        if( chsize( fd, truncate_len ) == -1 )
  2100. X        log_entry( "Unable to truncate the active2 file.\n" );
  2101. X        close( fd );
  2102. X    }
  2103. X#else
  2104. X    filename = file_exp( ACTIVE2 );
  2105. X    sprintf( line, "%s.new", filename );
  2106. X    if( (fp_active3 = fopen( line, "w" )) == Nullfp ) {
  2107. X        log_entry( "Unable to create the active2.new file.\n" );
  2108. X    } else if( (fp_active2 = fopen( filename, "r" )) == Nullfp ) {
  2109. X        fclose( fp_active3 );
  2110. X        unlink( line );
  2111. X        log_entry( "Unable to open the active2 file.\n" );
  2112. X    } else {
  2113. X        while( ftell( fp_active3 ) < truncate_len ) {
  2114. X        if( !fgets( line2, sizeof line2, fp_active2 ) ) {
  2115. X            break;
  2116. X        }
  2117. X        fputs( line2, fp_active3 );
  2118. X        }
  2119. X        sprintf( line2, "%s.old", filename );
  2120. X        rename( filename, line2 );
  2121. X        rename( line, filename );
  2122. X        fclose( fp_active2 );
  2123. X        fclose( fp_active3 );
  2124. X    }
  2125. X#endif /* not XENIX */
  2126. X#endif /* not TRUNCATE */
  2127. X    truncate_len = -1;
  2128. X    }
  2129. X
  2130. X    sprintf( line, "Processed %d group%s:  added %d article%s, expired %d.\n",
  2131. X    processed_groups, processed_groups == 1 ? nullstr : "s",
  2132. X    added_articles, added_articles == 1 ? nullstr : "s",
  2133. X    expired_articles );
  2134. X
  2135. X    if( processed_groups ) {
  2136. X    log_entry( line );
  2137. X    }
  2138. X
  2139. X    if( !daemon_delay ) {
  2140. X    putchar( '\n' );
  2141. X    fputs( line, stdout );
  2142. X    }
  2143. X    if( added_groups ) {
  2144. X    sprintf( line, "Turned %d group%s on.\n", added_groups,
  2145. X        added_groups == 1 ? nullstr : "s" );
  2146. X    log_entry( line );
  2147. X    if( !daemon_delay ) {
  2148. X        fputs( line, stdout );
  2149. X    }
  2150. X    }
  2151. X    if( removed_groups ) {
  2152. X    sprintf( line, "Turned %d group%s off.\n", removed_groups,
  2153. X        removed_groups == 1 ? nullstr : "s" );
  2154. X    log_entry( line );
  2155. X    if( !daemon_delay ) {
  2156. X        fputs( line, stdout );
  2157. X    }
  2158. X    }
  2159. X    extra_expire = FALSE;
  2160. X    rebuild = FALSE;
  2161. X}
  2162. X
  2163. X/*
  2164. X** ngmatch - newsgroup name matching
  2165. X**
  2166. X** returns NG_MATCH for a positive patch, NG_SKIP for a negative match,
  2167. X** and NG_DEFAULT if the group doesn't match at all.
  2168. X**
  2169. X** "all" in a pattern is a wildcard that matches exactly one word;
  2170. X** it does not cross "." (NGDELIM) delimiters.
  2171. X**
  2172. X** This matching code was borrowed from C news.
  2173. X*/
  2174. X
  2175. X#define ALL "all"            /* word wildcard */
  2176. X
  2177. X#define NGNEG '!'
  2178. X#define NGSEP ','
  2179. X#define NGDELIM '.'
  2180. X
  2181. Xint
  2182. Xngmatch( ngpat, grp )
  2183. Xchar *ngpat, *grp;
  2184. X{
  2185. X    register char *patp;        /* point at current pattern */
  2186. X    register char *patcomma;
  2187. X    register int depth;
  2188. X    register int faildeepest = 0, hitdeepest = 0;    /* in case no match */
  2189. X    register bool negation;
  2190. X
  2191. X    for( patp = ngpat; patp != Nullch; patp = patcomma ) {
  2192. X    negation = FALSE;
  2193. X    patcomma = index( patp, NGSEP );
  2194. X    if( patcomma != Nullch ) {
  2195. X        *patcomma = '\0';    /* will be restored below */
  2196. X    }
  2197. X    if( *patp == NGNEG ) {
  2198. X        ++patp;
  2199. X        negation = TRUE;
  2200. X    }
  2201. X    depth = onepatmatch( patp, grp ); /* try 1 pattern, 1 group */
  2202. X    if( patcomma != Nullch ) {
  2203. X        *patcomma++ = NGSEP;    /* point after the comma */
  2204. X    }
  2205. X    if( depth == 0 ) {        /* mis-match */
  2206. X        ;                /* ignore it */
  2207. X    } else if( negation ) {
  2208. X        /* record depth of deepest negated matched word */
  2209. X        if( depth > faildeepest ) {
  2210. X        faildeepest = depth;
  2211. X        }
  2212. X    } else {
  2213. X        /* record depth of deepest plain matched word */
  2214. X        if( depth > hitdeepest ) {
  2215. X        hitdeepest = depth;
  2216. X        }
  2217. X    }
  2218. X    }
  2219. X    if( hitdeepest > faildeepest ) {
  2220. X    return NG_MATCH;
  2221. X    } else if( faildeepest ) {
  2222. X    return NG_SKIP;
  2223. X    } else {
  2224. X    return NG_DEFAULT;
  2225. X    }
  2226. X}
  2227. X
  2228. X/*
  2229. X** Match a pattern against a group by looking at each word of pattern in turn.
  2230. X**
  2231. X** On a match, return the depth (roughly, ordinal number * k) of the rightmost
  2232. X** word that matches.  If group runs out first, the match fails; if pattern
  2233. X** runs out first, it succeeds.  On a failure, return zero.
  2234. X*/
  2235. Xint
  2236. Xonepatmatch( patp, grp )
  2237. Xchar *patp, *grp;
  2238. X{
  2239. X    register char *rpatwd;        /* used by word match (inner loop) */
  2240. X    register char *patdot, *grdot;    /* point at dots after words */
  2241. X    register char *patwd, *grwd;    /* point at current words */
  2242. X    register int depth = 0;
  2243. X
  2244. X    for( patwd = patp, grwd = grp;
  2245. X     patwd != Nullch && grwd != Nullch;
  2246. X     patwd = patdot, grwd = grdot
  2247. X    ) {
  2248. X    register bool match = FALSE;
  2249. X    register int incr = 20;
  2250. X
  2251. X    /* null-terminate words */
  2252. X    patdot = index(patwd, NGDELIM);
  2253. X    if( patdot != Nullch ) {
  2254. X        *patdot = '\0';        /* will be restored below */
  2255. X    }
  2256. X    grdot = index( grwd, NGDELIM );
  2257. X    if( grdot != Nullch ) {
  2258. X        *grdot = '\0';        /* will be restored below */
  2259. X    }
  2260. X    /*
  2261. X     * Match one word of pattern with one word of group.
  2262. X     * A pattern word of "all" matches any group word,
  2263. X     * but isn't worth as much.
  2264. X     */
  2265. X#ifdef FAST_STRCMP
  2266. X    match = STREQ( patwd, grwd );
  2267. X    if( !match && STREQ( patwd, ALL ) ) {
  2268. X        match = TRUE;
  2269. X        --incr;
  2270. X    }
  2271. X#else
  2272. X    for( rpatwd = patwd; *rpatwd == *grwd++; ) {
  2273. X        if( *rpatwd++ == '\0' ) {
  2274. X        match = TRUE;        /* literal match */
  2275. X        break;
  2276. X        }
  2277. X    }
  2278. X    if( !match ) {
  2279. X        /* ugly special case match for "all" */
  2280. X        rpatwd = patwd;
  2281. X        if( *rpatwd++ == 'a' && *rpatwd++ == 'l'
  2282. X         && *rpatwd++ == 'l' && *rpatwd   == '\0' ) {
  2283. X        match = TRUE;
  2284. X         --incr;
  2285. X        }
  2286. X    }
  2287. X#endif                /* FAST_STRCMP */
  2288. X
  2289. X    if( patdot != Nullch ) {
  2290. X        *patdot++ = NGDELIM;    /* point after the dot */
  2291. X    }
  2292. X    if( grdot != Nullch ) {
  2293. X        *grdot++ = NGDELIM;
  2294. X    }
  2295. X    if( !match ) {
  2296. X        depth = 0;        /* words differed - mismatch */
  2297. X        break;
  2298. X    }
  2299. X    depth += incr;
  2300. X    }
  2301. X    /* if group name ran out before pattern, then match fails */
  2302. X    if( grwd == Nullch && patwd != Nullch ) {
  2303. X    depth = 0;
  2304. X    }
  2305. X    return depth;
  2306. X}
  2307. X
  2308. X/* Generate a log entry with timestamp.
  2309. X*/
  2310. X/*VARARGS1*/
  2311. Xvoid
  2312. Xlog_entry( fmt, arg1, arg2 )
  2313. Xchar *fmt;
  2314. Xlong arg1;
  2315. Xlong arg2;
  2316. X{
  2317. X    time_t now;
  2318. X    char *ctime();
  2319. X
  2320. X    (void) time( &now );
  2321. X    fprintf( fp_log, "%.12s ", ctime( &now )+4 );
  2322. X    fprintf( fp_log, fmt, arg1, arg2 );
  2323. X    fflush( fp_log );
  2324. X}
  2325. X
  2326. X/* Generate an log entry, with 'E'rror flagging (non-daemon mode), time-stamp,
  2327. X** and newsgroup name.
  2328. X*/
  2329. X/*VARARGS1*/
  2330. Xvoid
  2331. Xlog_error( fmt, arg1, arg2, arg3 )
  2332. Xchar *fmt;
  2333. Xlong arg1;
  2334. Xlong arg2;
  2335. Xlong arg3;
  2336. X{
  2337. X    log_entry( "%s: ", line );
  2338. X    fprintf( fp_log, fmt, arg1, arg2, arg3 );
  2339. X    fflush( fp_log );
  2340. X    if( *fmt == '*' ) {
  2341. X    grevious_error = TRUE;
  2342. X    if( !daemon_delay ) {
  2343. X        putchar( 'E' );
  2344. X    }
  2345. X    }
  2346. X    else {
  2347. X    if( !daemon_delay ) {
  2348. X        putchar( 'e' );
  2349. X    }
  2350. X    }
  2351. X}
  2352. X
  2353. X#ifndef RENAME
  2354. Xint
  2355. Xrename( old, new )
  2356. Xchar    *old, *new;
  2357. X{
  2358. X    struct stat st;
  2359. X
  2360. X    if( stat( old, &st ) == -1 ) {
  2361. X    return -1;
  2362. X    }
  2363. X    if( unlink( new ) == -1 && errno != ENOENT ) {
  2364. X    return -1;
  2365. X    }
  2366. X    if( link( old, new ) == -1 ) {
  2367. X    return -1;
  2368. X    }
  2369. X    if( unlink( old ) == -1 ) {
  2370. X    int e = errno;
  2371. X    (void) unlink( new );
  2372. X    errno = e;
  2373. X    return -1;
  2374. X    }
  2375. X    return 0;
  2376. X}
  2377. X#endif /*RENAME*/
  2378. END_OF_FILE
  2379.   if test 29761 -ne `wc -c <'mthreads.c'`; then
  2380.     echo shar: \"'mthreads.c'\" unpacked with wrong size!
  2381.   fi
  2382.   # end of 'mthreads.c'
  2383. fi
  2384. echo shar: End of archive 6 \(of 14\).
  2385. cp /dev/null ark6isdone
  2386. MISSING=""
  2387. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do
  2388.     if test ! -f ark${I}isdone ; then
  2389.     MISSING="${MISSING} ${I}"
  2390.     fi
  2391. done
  2392. if test "${MISSING}" = "" ; then
  2393.     echo You have unpacked all 14 archives.
  2394.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2395. else
  2396.     echo You still must unpack the following archives:
  2397.     echo "        " ${MISSING}
  2398. fi
  2399. exit 0
  2400.